【问题标题】:CALayer vertically centered text XCode 11.6CALayer垂直居中文本XCode 11.6
【发布时间】:2020-08-19 02:08:40
【问题描述】:

我正在构建一个 PDF 文档,并且图像和文本正在作为 CALayers 写入视图。我需要在 CATextLayer 的边框内垂直居中我的文本。我正在使用我在 2016 年找到的一个类,如下所示,它覆盖了 draw 函数。我想知道是否有任何新技巧可以使这项工作发挥作用?

正如您在运行此代码时看到的那样,单元格 2 的文本甚至没有显示,单元格 3 的文本也没有垂直居中。

非常感谢任何可以帮助我的人。

//
//  ViewController.swift
//  CALayers Example
//
//  Created by Thomas Carroll on 8/18/20.
//  Copyright © 2020 Thomas Carroll. All rights reserved.
//

import Cocoa

class ViewController: NSViewController {

    let myLayers = MyLayers()
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.wantsLayer = true
        self.view.layer?.addSublayer(myLayers.insertGrid())
        self.view.layer?.addSublayer(myLayers.insertText())

        // Do any additional setup after loading the view.
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }


}
//
//  MyLayers.swift
//  CALayers Example
//
//  Created by Thomas Carroll on 8/18/20.
//  Copyright © 2020 Thomas Carroll. All rights reserved.
//

import Cocoa

// Set up constant variables
let pageWidth:Float = 72*8.5
let pageHeight:Float = 72*11
// Set up coordinates
let leftX = Int(pageWidth/2-72*2.5)
let col1X = Int(leftX+72)
let col2X = Int(col1X+72)
let col3X = Int(col2X+72)
let col4X = Int(col3X+72)
let rightX = Int(col4X+72)
let bottomY = Int(pageHeight/2-72*2.5)
let row4Y = Int(bottomY+72)
let row3Y = Int(row4Y+72)
let row2Y = Int(row3Y+72)
let row1Y = Int(row2Y+72)
let topY = Int(row1Y+72)

// Set the extension to draw Bezier paths into a CAShapeLayer
extension NSBezierPath {
    // Credit - Henrick - 9/18/2016
    // https://stackoverflow.com/questions/1815568/how-can-i-convert-nsbezierpath-to-cgpath
    public var cgPath: CGPath {
        let path = CGMutablePath()
        var points = [CGPoint](repeating: .zero, count: 3)
        for i in 0 ..< self.elementCount {
            let type = self.element(at: i, associatedPoints: &points)
            switch type {
            case .moveTo:
                path.move(to: points[0])
            case .lineTo:
                path.addLine(to: points[0])
            case .curveTo:
                path.addCurve(to: points[2], control1: points[0], control2: points[1])
            case .closePath:
                path.closeSubpath()
            @unknown default:
                print("Error occured in NSBezierPath extension.")
            }
        }
        return path
    }
}

class MyLayers {
    
    class VerticallyAlignedTextLayer : CATextLayer {

        func calculateMaxLines() -> Int {
            let maxSize = CGSize(width: frame.size.width, height: frame.size.height)
            let font = NSFont(descriptor: self.font!.fontDescriptor, size: self.fontSize)
            let charSize = (font?.capHeight)!
            let text = (self.string ?? "") as! NSString
            let textSize = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font!], context: nil)
            let linesRoundedUp = Int(ceil(textSize.height/charSize))
            return linesRoundedUp
        }
        
        override func draw(in context: CGContext) {
            let height = self.bounds.size.height
            let fontSize = self.fontSize
            let lines = CGFloat(calculateMaxLines())
            let yDiff = (height - lines * fontSize) / 2 - lines * fontSize / 10

            context.saveGState()
            context.translateBy(x: 0, y: yDiff) // Use -yDiff when in non-flipped coordinates (like macOS's default)
            super.draw(in: context)
            context.restoreGState()
        }
    }
        
    func insertGrid() -> CALayer {
        
        /*
         Draws a single table grid of 25 boxes (5 high by 5 wide)
         centered on a letter sized page
         */
        
        // Create a new shape layer for the grid
        let gridLayer = CAShapeLayer()
        
        // Create the path
        let gridPath = NSBezierPath()

        // Assign the grid fill and stroke colors
        gridLayer.strokeColor = NSColor.purple.cgColor
        gridLayer.fillColor = NSColor.clear.cgColor

        // Draw the paths for the grid
        // Create the outside box
        gridPath.move(to: CGPoint(x: leftX, y: bottomY)) // Bottom left corner
        gridPath.line(to: CGPoint(x: leftX, y: topY)) // Column 1, left line
        gridPath.line(to: CGPoint(x: rightX, y: topY)) // Row 1, top line
        gridPath.line(to: CGPoint(x: rightX, y: bottomY)) // Column 5 right line
        gridPath.line(to: CGPoint(x: leftX, y: bottomY)) // Row 5 bottom line
        
        // Add in column lines
        gridPath.move(to: CGPoint(x: col1X, y: topY)) // Between columns 1 & 2
        gridPath.line(to: CGPoint(x: col1X, y: bottomY)) // Line between columns 1 & 2
        gridPath.move(to: CGPoint(x: col2X, y: topY)) // Between columns 2 & 3
        gridPath.line(to: CGPoint(x: col2X, y: bottomY)) // Line between columns 2 & 3
        gridPath.move(to: CGPoint(x: col3X, y: topY)) // Between columns 3 & 4
        gridPath.line(to: CGPoint(x: col3X, y: bottomY)) // Line between columns 3 & 4
        gridPath.move(to: CGPoint(x: col4X, y: topY)) // Between columns 4 & 5
        gridPath.line(to: CGPoint(x: col4X, y: bottomY)) // Line between columns 4 & 5
        // Add in row lines
        gridPath.move(to: CGPoint(x: leftX, y: row1Y)) // Between rows 1 & 2
        gridPath.line(to: CGPoint(x: rightX, y: row1Y)) // Line between rows 1 & 2
        gridPath.move(to: CGPoint(x: leftX, y: row2Y)) // Between rows 2 & 3
        gridPath.line(to: CGPoint(x: rightX, y: row2Y)) // Line between rows 2 & 3
        gridPath.move(to: CGPoint(x: leftX, y: row3Y)) // Between rows 3 & 4
        gridPath.line(to: CGPoint(x: rightX, y: row3Y)) // Line between rows 3 & 4
        gridPath.move(to: CGPoint(x: leftX, y: row4Y)) // Between rows 4 & 5
        gridPath.line(to: CGPoint(x: rightX, y: row4Y)) // Line between rows 4 & 5

        // Close the path
        gridPath.close()
        // Add grid to layer (note the use of the cgPath extension)
        gridLayer.path = gridPath.cgPath

        return gridLayer
    }

    func insertText() -> CALayer {
        
        // Create a CALayer to add the textLayer to
        let myCALayer = CALayer()
        // Set up an array to hold the x coordinate for each column
        let colPosX = [leftX, col1X, col2X, col3X, col4X]
        // Set up an array to hold the y coordinate for the first card
        let rowPosY = [row1Y, row2Y, row3Y, row4Y, bottomY]
        // Set some default text to be used in the textLayers
        let cellText = ["This is some cell 1 text", "Cell 2 text", "This is text cell 3"]
        
        for i in (0...2) {
            let textLayer = VerticallyAlignedTextLayer()
            textLayer.string = cellText[i]
            textLayer.fontSize = 14
            // Set the frame to be 1 pixel smaller than the grid cell to provide 1px padding
            textLayer.frame = CGRect(origin: CGPoint(x: Int(colPosX[i])+1, y: Int(rowPosY[i])+1), size: CGSize(width: 70, height: 70))
            textLayer.alignmentMode = .center
            textLayer.isWrapped = true
            textLayer.foregroundColor = NSColor.black.cgColor
            textLayer.backgroundColor = NSColor.clear.cgColor
            textLayer.truncationMode = .none
            myCALayer.addSublayer(textLayer)
        }
        
        return myCALayer
    }
}

【问题讨论】:

  • 如果我将“单元格 2 文本”更改为“某些单元格 2 文本”单元格 2 然后显示文本。
  • 复杂代码;如果您想查看原始文本,请尝试将 YDiff 设为负值。当它是一个大的正值时,文本不会正确转置。
  • 我改变了它,但仍然没有任何区别。

标签: swift cocoa calayer


【解决方案1】:

这是我在我的系统上得到的。

【讨论】:

  • 注意 row1/col1 的单元格如何不居中?它们都不是真正居中的。如果没有比这更好的了,那么我可能不得不自己编写,通过定位框架使文本垂直居中来使文本居中。
  • 我的 Cell 2 文本没有出现。我尝试像你一样在文本字符串数组“hello”中输入第四个元素,但这也没有显示给我。
  • 如果有帮助,我愿意发布代码(使用 CommandLine 或者您可以使用它创建 Xcode 应用程序)。
  • 当然。也许它会让我知道我做错了什么。
【解决方案2】:

我所做的只是将一些打印语句添加到 func draw() 以便我可以看到发生了什么,然后在 YDiff 计算前面添加一个负号。在我看来,它没有显示文本,因为 Y 坐标导致它被翻译到你的框之外的某个地方。此外,.translateBy 后面的注释说使用负 YDiff。

override func draw(in context: CGContext) {
    let height = self.bounds.size.height
    print("height = \(height)")
    let fontSize = self.fontSize
    let lines = CGFloat(calculateMaxLines())
    print("lines = \(lines)")
    let yDiff = -(height - lines * fontSize) / 2 - lines * fontSize / 10
    print("yDiff = \(yDiff)")
    context.saveGState()
    context.translateBy(x: 0, y: yDiff) // Use -yDiff when in non-flipped coordinates (like macOS's default)
    super.draw(in: context)
    print("draw called.")
    context.restoreGState()
    }

【讨论】:

  • 在 yDiff 计算中,将第二个除数从 10 更改为 5 会使文本更接近垂直中心。
  • 是的,除以 5 而不是 10 会使它在单元格中更居中。此外,该示例在 context.translateBy 方法调用中具有负 yDiff。通过在 let yDiff 语句中将其作为否定,它现在出现在屏幕上的正确位置。当let yDiff = -(height - lines * fontSize) / 2 - lines * fontSize / 5 yDiff 等于 -9.8。但是,如果我没有在那个调用中放入负数,然后在 context.translateBy(x:0, y: -yDiff) 调用中放入负数,它等于 -23.8。
  • 我没有在两者中添加否定;让 yDiff = -()。我保留了 context.translateBy 原样。
【解决方案3】:

以下代码是我使用的,但您似乎可以使用它。唯一的变化是上面提到的 func draw() 。您可以从终端运行此代码或使用演示的主要部分创建自己的 Xcode 应用程序,并将 Xcode 的 AppDelegate 替换为演示的代码并添加其他类。

/*
  To run in Terminal: swiftc calayer.swift  -framework Cocoa -o calayer && ./calayer 
*/

import Cocoa

// Set up constant variables
let pageWidth:Float = 72*8.5
let pageHeight:Float = 72*11
// Set up coordinates
let leftX = Int(pageWidth/2-72*2.5)
let col1X = Int(leftX+72)
let col2X = Int(col1X+72)
let col3X = Int(col2X+72)
let col4X = Int(col3X+72)
let rightX = Int(col4X+72)
let bottomY = Int(pageHeight/2-72*2.5)
let row4Y = Int(bottomY+72)
let row3Y = Int(row4Y+72)
let row2Y = Int(row3Y+72)
let row1Y = Int(row2Y+72)
let topY = Int(row1Y+72)

// Set the extension to draw Bezier paths into a CAShapeLayer
extension NSBezierPath {
 // Credit - Henrick - 9/18/2016
 // https://stackoverflow.com/questions/1815568/how-can-i-convert-nsbezierpath-to-cgpath
 public var cgPath: CGPath {
   let path = CGMutablePath()
   var points = [CGPoint](repeating: .zero, count: 3)
   for i in 0 ..< self.elementCount {
     let type = self.element(at: i, associatedPoints: &points)
       switch type {
         case .moveTo:
           path.move(to: points[0])
         case .lineTo:
           path.addLine(to: points[0])
         case .curveTo:
           path.addCurve(to: points[2], control1: points[0], control2: points[1])
         case .closePath:
           path.closeSubpath()
         @unknown default:
           print("Error occured in NSBezierPath extension.")
         }
    }
   return path
  }
}

class MyLayers {
    
  class VerticallyAlignedTextLayer : CATextLayer {

  func calculateMaxLines() -> Int {
   let maxSize = CGSize(width: frame.size.width, height: frame.size.height)
   let font = NSFont(descriptor: self.font!.fontDescriptor, size: self.fontSize)
   let charSize = (font?.capHeight)!
   let text = (self.string ?? "") as! NSString
   let textSize = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font!], context: nil)
   let linesRoundedUp = Int(ceil(textSize.height/charSize))
   return linesRoundedUp
   }
        
   override func draw(in context: CGContext) {
    let height = self.bounds.size.height
    print("height = \(height)")
    let fontSize = self.fontSize
    let lines = CGFloat(calculateMaxLines())
    print("lines = \(lines)")
    //let yDiff = -(height - lines * fontSize) / 2 - lines * fontSize / 10
    let yDiff = -(height - lines * fontSize) / 2 - lines * fontSize / 5
    print("yDiff = \(yDiff)")
    context.saveGState()
    context.translateBy(x: 0, y: yDiff) // Use -yDiff when in non-flipped coordinates (like macOS's default)
    super.draw(in: context)
    print("draw called.")
    print("==========================")
    context.restoreGState()
    }
  }
        
 func insertGrid() -> CALayer {
        
  /*
    Draws a single table grid of 25 boxes (5 high by 5 wide)
    centered on a letter sized page
  */
        
  // Create a new shape layer for the grid
   let gridLayer = CAShapeLayer()
        
  // Create the path
   let gridPath = NSBezierPath()

  // Assign the grid fill and stroke colors
  gridLayer.strokeColor = NSColor.purple.cgColor
  gridLayer.fillColor = NSColor.clear.cgColor

  // Draw the paths for the grid
  // Create the outside box
  gridPath.move(to: CGPoint(x: leftX, y: bottomY)) // Bottom left corner
  gridPath.line(to: CGPoint(x: leftX, y: topY)) // Column 1, left line
  gridPath.line(to: CGPoint(x: rightX, y: topY)) // Row 1, top line
  gridPath.line(to: CGPoint(x: rightX, y: bottomY)) // Column 5 right line
  gridPath.line(to: CGPoint(x: leftX, y: bottomY)) // Row 5 bottom line
        
  // Add in column lines
  gridPath.move(to: CGPoint(x: col1X, y: topY)) // Between columns 1 & 2
  gridPath.line(to: CGPoint(x: col1X, y: bottomY)) // Line between columns 1 & 2
  gridPath.move(to: CGPoint(x: col2X, y: topY)) // Between columns 2 & 3
  gridPath.line(to: CGPoint(x: col2X, y: bottomY)) // Line between columns 2 & 3
  gridPath.move(to: CGPoint(x: col3X, y: topY)) // Between columns 3 & 4
  gridPath.line(to: CGPoint(x: col3X, y: bottomY)) // Line between columns 3 & 4
  gridPath.move(to: CGPoint(x: col4X, y: topY)) // Between columns 4 & 5
  gridPath.line(to: CGPoint(x: col4X, y: bottomY)) // Line between columns 4 & 5
  // Add in row lines
  gridPath.move(to: CGPoint(x: leftX, y: row1Y)) // Between rows 1 & 2
  gridPath.line(to: CGPoint(x: rightX, y: row1Y)) // Line between rows 1 & 2
  gridPath.move(to: CGPoint(x: leftX, y: row2Y)) // Between rows 2 & 3
  gridPath.line(to: CGPoint(x: rightX, y: row2Y)) // Line between rows 2 & 3
  gridPath.move(to: CGPoint(x: leftX, y: row3Y)) // Between rows 3 & 4
  gridPath.line(to: CGPoint(x: rightX, y: row3Y)) // Line between rows 3 & 4
  gridPath.move(to: CGPoint(x: leftX, y: row4Y)) // Between rows 4 & 5
  gridPath.line(to: CGPoint(x: rightX, y: row4Y)) // Line between rows 4 & 5

  // Close the path
  gridPath.close()
  // Add grid to layer (note the use of the cgPath extension)
  gridLayer.path = gridPath.cgPath

  return gridLayer
 }

 func insertText() -> CALayer {
        
 // Create a CALayer to add the textLayer to
 let myCALayer = CALayer()
 // Set up an array to hold the x coordinate for each column
 let colPosX = [leftX, col1X, col2X, col3X, col4X]
 // Set up an array to hold the y coordinate for the first card
 let rowPosY = [row1Y, row2Y, row3Y, row4Y, bottomY]
 // Set some default text to be used in the textLayers
 // let cellText = ["This is some cell 1 text", "Some cell 2 text", "This is text cell 3"]
 let cellText = ["This is some cell 1 text", "Cell 2 text", "This is text cell 3", "hello"]
        
 for i in (0...3) {
  let textLayer = VerticallyAlignedTextLayer()
  textLayer.string = cellText[i]
  print(cellText[i])
  textLayer.fontSize = 14
  // Set the frame to be 1 pixel smaller than the grid cell to provide 1px padding
  textLayer.frame = CGRect(origin: CGPoint(x: Int(colPosX[i])+1, y: Int(rowPosY[i])+1), size: CGSize(width: 70, height: 70))
  textLayer.alignmentMode = .center
  textLayer.isWrapped = true
  textLayer.foregroundColor = NSColor.black.cgColor
  textLayer.backgroundColor = NSColor.clear.cgColor
  textLayer.truncationMode = .none
  myCALayer.addSublayer(textLayer)
  }  
  print("=======================")    
  return myCALayer
 }
}


class AppDelegate: NSObject, NSApplicationDelegate {
var window:NSWindow!

@objc func myBtnAction(_ sender:AnyObject ) {
  NSSound.beep()
}

func buildMenu() {
 let mainMenu = NSMenu()
 NSApp.mainMenu = mainMenu
 // **** App menu **** //
 let appMenuItem = NSMenuItem()
 mainMenu.addItem(appMenuItem)
 let appMenu = NSMenu()
 appMenuItem.submenu = appMenu
 appMenu.addItem(withTitle: "Quit", action:#selector(NSApplication.terminate), keyEquivalent: "q") 
}
    
func buildWnd() {
    
let _wndW : CGFloat = 650
let _wndH : CGFloat = 700

 window = NSWindow(contentRect:NSMakeRect(0,0,_wndW,_wndH),styleMask:[.titled, .closable, .miniaturizable, .resizable], backing:.buffered, defer:false)
 window.center()
 window.title = "Swift Test Window"
 window.makeKeyAndOrderFront(window)

 let view = NSView()
 let myLayers = MyLayers()
 view.frame = NSMakeRect(20, 60, _wndW - 40, _wndH - 80)
 view.wantsLayer = true
 view.layer?.addSublayer(myLayers.insertGrid())
 view.layer?.addSublayer(myLayers.insertText())
 window.contentView!.addSubview (view)

// **** Quit btn **** //
let quitBtn = NSButton (frame:NSMakeRect( _wndW - 50, 10, 40, 40 ))
 quitBtn.bezelStyle = .circular
 quitBtn.autoresizingMask = [.minXMargin,.maxYMargin]
 quitBtn.title = "Q"
 quitBtn.action = #selector(NSApplication.terminate)
 window.contentView!.addSubview(quitBtn)
}
 
func applicationDidFinishLaunching(_ notification: Notification) {
 buildMenu()
 buildWnd()
}

func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
 return true
}

}
let appDelegate = AppDelegate()

// **** main.swift **** //
let app = NSApplication.shared
app.delegate = appDelegate
app.setActivationPolicy(.regular)
app.activate(ignoringOtherApps:true)
app.run()


【讨论】:

    【解决方案4】:

    Swift 5.3

    好的,我终于知道如何做我需要的了。具体来说,我需要更改 textLayer 的 fontSize 以使其水平和垂直适合边界框,同时在边界框内垂直居中。为此,我找到了一些检查属性字符串的 boundingRect 的代码。另请注意,我更改了 yDiff 计算,其中 fontSize 除以 6.5 而不是 10,它对我来说垂直定位文本更好。

    class MyLayers {
        
        class VerticallyAlignedTextLayer : CATextLayer {
    
            func calculateMaxLines() -> Int {
                let maxSize = CGSize(width: frame.size.width, height: frame.size.width)
                let font = NSFont(descriptor: self.font!.fontDescriptor, size: self.fontSize)
                let charSize = floor(font!.capHeight)
                let text = (self.string ?? "") as! NSString
                let textSize = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font!], context: nil)
                let linesRoundedUp = Int(floor(textSize.height/charSize))
                return linesRoundedUp
            }
            
            override func draw(in context: CGContext) {
                let height = self.bounds.size.height
                let fontSize = self.fontSize
                let lines = CGFloat(calculateMaxLines())
                let yDiff = -(height - lines * fontSize) / 2 - lines * fontSize / 6.5 // Use -(height - lines * fontSize) / 2 - lines * fontSize / 6.5 when in non-flipped coordinates (like macOS's default)
    
                context.saveGState()
                context.translateBy(x: 0, y: yDiff)
                super.draw(in: context)
                context.restoreGState()
            }
        }
            
        func insertGrid() -> CALayer {
            
            /*
             Draws a single table grid of 25 boxes (5 high by 5 wide)
             centered on a letter sized page
             */
            
            // Create a new shape layer for the grid
            let gridLayer = CAShapeLayer()
            
            // Create the path
            let gridPath = NSBezierPath()
    
            // Assign the grid fill and stroke colors
            gridLayer.strokeColor = NSColor.purple.cgColor
            gridLayer.fillColor = NSColor.white.cgColor
    
            // Draw the paths for the grid
            // Create the outside box
            gridPath.move(to: CGPoint(x: leftX, y: bottomY)) // Bottom left corner
            gridPath.line(to: CGPoint(x: leftX, y: topY)) // Column 1, left line
            gridPath.line(to: CGPoint(x: rightX, y: topY)) // Row 1, top line
            gridPath.line(to: CGPoint(x: rightX, y: bottomY)) // Column 5 right line
            gridPath.line(to: CGPoint(x: leftX, y: bottomY)) // Row 5 bottom line
            //gridPath.close()
            //gridLayer.path = gridPath.cgPath
            
            // Add in column lines
            gridPath.move(to: CGPoint(x: col1X, y: topY)) // Between columns 1 & 2
            gridPath.line(to: CGPoint(x: col1X, y: bottomY)) // Line between columns 1 & 2
            gridPath.move(to: CGPoint(x: col2X, y: topY)) // Between columns 2 & 3
            gridPath.line(to: CGPoint(x: col2X, y: bottomY)) // Line between columns 2 & 3
            gridPath.move(to: CGPoint(x: col3X, y: topY)) // Between columns 3 & 4
            gridPath.line(to: CGPoint(x: col3X, y: bottomY)) // Line between columns 3 & 4
            gridPath.move(to: CGPoint(x: col4X, y: topY)) // Between columns 4 & 5
            gridPath.line(to: CGPoint(x: col4X, y: bottomY)) // Line between columns 4 & 5
            // Add in row lines
            gridPath.move(to: CGPoint(x: leftX, y: row1Y)) // Between rows 1 & 2
            gridPath.line(to: CGPoint(x: rightX, y: row1Y)) // Line between rows 1 & 2
            gridPath.move(to: CGPoint(x: leftX, y: row2Y)) // Between rows 2 & 3
            gridPath.line(to: CGPoint(x: rightX, y: row2Y)) // Line between rows 2 & 3
            gridPath.move(to: CGPoint(x: leftX, y: row3Y)) // Between rows 3 & 4
            gridPath.line(to: CGPoint(x: rightX, y: row3Y)) // Line between rows 3 & 4
            gridPath.move(to: CGPoint(x: leftX, y: row4Y)) // Between rows 4 & 5
            gridPath.line(to: CGPoint(x: rightX, y: row4Y)) // Line between rows 4 & 5
    
            // Close the path
            gridPath.close()
            // Add grid to layer (note the use of the cgPath extension)
            gridLayer.path = gridPath.cgPath
    
            return gridLayer
        }
        
        func sizeOfRect(string: NSString, fontSize: CGFloat) -> Int {
            /* Credit to Jake Marsh - 12/10/2015
                https://littlebitesofcocoa.com/144-drawing-multiline-strings
             
                Return the height of a boundingRect for a specified string at a specified fontSize
             */
            let cellFontSize:CGFloat = fontSize
            let cellFont:NSFont = NSFont.systemFont(ofSize: cellFontSize, weight: .regular)
            let cellParagraphStyle = NSMutableParagraphStyle()
            let cellTextAttributes = [NSAttributedString.Key.font: cellFont, NSAttributedString.Key.paragraphStyle: cellParagraphStyle]
            let cellDrawingOptions: NSString.DrawingOptions = [
            .usesLineFragmentOrigin, .usesFontLeading]
            cellParagraphStyle.lineHeightMultiple = 1.0
            cellParagraphStyle.lineBreakMode = .byWordWrapping
            
            return Int(string.boundingRect(with: CGSize(width: 70, height: CGFloat.infinity), options: cellDrawingOptions, attributes: cellTextAttributes).height)
        }
    
        func insertText() -> CALayer {
            
            // Create a CALayer to add the textLayer to
            let myCALayer = CALayer()
            // Set up an array to hold the x coordinate for each column
            let colPosX = [leftX, col1X, col2X, col3X, col4X]
            // Set up an array to hold the y coordinate for the first card
            let rowPosY = [row1Y, row2Y, row3Y, row4Y, bottomY]
            // Set some default text to be used in the textLayers
            let cellText = ["This is some cell 1 text that is kind of long", "Cell 2 text", "This is text cell 3", "Some really really long text"]
            
            for i in (0...3) {
                // Create a vertically centered textLayer
                let textLayer = VerticallyAlignedTextLayer()
                // Set up the initial font size for the text
                var fontSize:CGFloat = 14
                // Assign a string to the textLayer
                textLayer.string = cellText[i]
                // Check the vertical hieght of a rectangle that would contain the text based on the current fontSize.  If the text is taler than the specific box height, reduce the fontSize my a half point until it is within the specified height of the box.
                while sizeOfRect(string: cellText[i] as NSString, fontSize: fontSize) > 68 {
                    fontSize -= 0.5
                }
                // Assign the adjusted fontSize to the textLayer
                textLayer.fontSize = fontSize
                // Set the frame to be 4 pixel smaller than the grid cell to provide 2px padding
                textLayer.frame = CGRect(origin: CGPoint(x: Int(colPosX[i])+2, y: Int(rowPosY[i])+2), size: CGSize(width: 68, height: 68))
                textLayer.alignmentMode = .center
                textLayer.isWrapped = true
                textLayer.foregroundColor = NSColor.black.cgColor
                textLayer.backgroundColor = NSColor.clear.cgColor
                textLayer.truncationMode = .none
                myCALayer.addSublayer(textLayer)
            }
            
            return myCALayer
            
        }
    
    }
    

    【讨论】:

    • 四个中只有一个垂直居中在我的系统上,但你是必须对此感到满意的那个。很高兴你把它修好了。
    猜你喜欢
    • 2013-10-15
    • 2015-09-16
    • 2015-06-06
    • 1970-01-01
    • 2023-03-05
    • 2014-03-02
    • 2011-02-03
    相关资源
    最近更新 更多