ing.conti 的回答,但在 Swift 4 中:
var H:CGFloat = 0
// Create the framesetter with the attributed string.
let framesetter = CTFramesetterCreateWithAttributedString(attributedString as! CFMutableAttributedString)
let box:CGRect = CGRect.init(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude)
let startIndex:CFIndex = 0
let path:CGMutablePath = CGMutablePath()
path.addRect(box)
// Create a frame for this column and draw it.
let frame:CTFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(startIndex, 0), path, nil)
// Start the next frame at the first character not visible in this frame.
//CFRange frameRange = CTFrameGetVisibleStringRange(frame);
//startIndex += frameRange.length;
let lineArray:CFArray = CTFrameGetLines(frame)
let lineCount:CFIndex = CFArrayGetCount(lineArray)
var h:CGFloat = 0
var ascent:CGFloat = 0
var descent:CGFloat = 0
var leading:CGFloat = 0
for j in 0..<lineCount {
let currentLine = unsafeBitCast(CFArrayGetValueAtIndex(lineArray, j), to: CTLine.self)
CTLineGetTypographicBounds(currentLine, &ascent, &descent, &leading)
h = ascent + descent + leading;
H+=h;
}
return H;
我确实尝试将其与 Objective C 代码保持为 1:1,但 Swift 在处理指针时并没有那么好,因此需要进行一些更改才能进行转换。
我还做了一些基准测试,将这段代码(和它的 ObjC 对应物)与另一种高度方法进行比较。作为提示,我使用了一个巨大且非常复杂的属性字符串作为输入,并且也在 sim 上做了,所以时间本身是没有意义的,但是相对速度是正确的。
Runtime for 1000 iterations (ms) BoundsForRect: 8909.763097763062
Runtime for 1000 iterations (ms) layoutManager: 7727.7010679244995
Runtime for 1000 iterations (ms) CTFramesetterSuggestFrameSizeWithConstraints: 1968.9229726791382
Runtime for 1000 iterations (ms) CTFramesetterCreateFrame ObjC: 1941.6030206680298
Runtime for 1000 iterations (ms) CTFramesetterCreateFrame-Swift: 1912.694974899292