我一直在研究使用Core Text的视图,并且在使用字形位置方面遇到了一些麻烦。我想要做的是在UILabel上覆盖一些文本路径,但我发现一切都有点偏。与标签相比,我是否遗漏了与职位相关的内容?谢谢!
注意:绿色文本使用核心图形渲染,而白色是其背后的标签。
func renderString(from text: String, shapeLayer :(CAShapeLayer) -> Void) -> ([CGPoint], [CAShapeLayer]) {
var letterLayers :[CAShapeLayer] = []
var letterPositions :[CGPoint] = []
let runFont = CTFontCreateWithName(font.fontName as CFString?, font.pointSize, nil)//unsafeBitCast(CFDictionaryGetValue(CTRunGetAttributes(run), Unmanaged.passUnretained(kCTFontAttributeName).toOpaque()), to: CTFont.self)
var alignment = CTTextAlignment.left
let alignmentSetting = [CTParagraphStyleSetting(spec: .alignment, valueSize: MemoryLayout.size(ofValue: alignment), value: &alignment)]
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, alignmentSetting.count)
let attributedString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0)
CFAttributedStringReplaceString(attributedString, CFRangeMake(0, 0), text as CFString!)
CFAttributedStringSetAttribute(attributedString, CFRangeMake(0, CFAttributedStringGetLength(attributedString)), kCTFontAttributeName, runFont)
CFAttributedStringSetAttribute(attributedString, CFRangeMake(0, CFAttributedStringGetLength(attributedString)), kCTParagraphStyleAttributeName, paragraphStyle)
let framesetter = CTFramesetterCreateWithAttributedString(attributedString!)
let path = UIBezierPath(rect: bounds).cgPath
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)
let lines = CTFrameGetLines(frame)
for index in 0..<CFArrayGetCount(lines) {
var line = unsafeBitCast(CFArrayGetValueAtIndex(lines, index), to: CTLine.self)
line = CTLineCreateJustifiedLine(line, 0.0, Double(bounds.width))!
var lineOrigin :CGPoint = CGPoint()
CTFrameGetLineOrigins(frame, CFRangeMake(index, 1), &lineOrigin)
var ascent :CGFloat = CGFloat()
var descent :CGFloat = CGFloat()
var leading :CGFloat = CGFloat()
CTLineGetTypographicBounds(line, &ascent, &descent, &leading)
let lineHeight = ascent + descent + leading
let runArray = CTLineGetGlyphRuns(line)
for index in 0..<CFArrayGetCount(runArray) {
let run = unsafeBitCast(CFArrayGetValueAtIndex(runArray, index), to: CTRun.self)
var glyphs = [CGGlyph](repeating: CGGlyph(), count: text.characters.count)
var positions = [CGPoint](repeating: CGPoint(), count: text.characters.count)
let range = CFRangeMake(0, 0)
CTRunGetGlyphs(run, range, &glyphs)
CTRunGetPositions(run, range, &positions)
var glyphBoundingRects = [CGRect](repeating: CGRect(), count: text.characters.count)
CTFontGetBoundingRectsForGlyphs(runFont, .default, glyphs, &glyphBoundingRects, text.characters.count)
for glyphIndex in 0..<glyphs.count {
let glyph = glyphs[glyphIndex]
let position = positions[glyphIndex]
let glyphBounds :CGRect = glyphBoundingRects[glyphIndex]
// let offset = CTLineGetOffsetForStringIndex(line, glyphIndex, nil)
if let letter = CTFontCreatePathForGlyph(runFont, glyph, nil) {
let letterLayer = CAShapeLayer()
letterLayer.path = letter
letterLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
letterLayer.isGeometryFlipped = true
letterLayer.bounds = glyphBounds
let x = lineOrigin.x + position.x + glyphBounds.minX + glyphBounds.width / 2
let y = position.y + lineHeight - lineOrigin.y - letterLayer.bounds.height / 2
letterLayer.position = CGPoint(x: x, y: y)
// letterLayer.backgroundColor = UIColor.red.cgColor
letterLayer.fillColor = UIColor.green.cgColor
letterLayers.append(letterLayer)
letterPositions.append(position)
shapeLayer(letterLayer)
} else {
print("Letter is nil.")
}
}
}
}
return (letterPositions, letterLayers)
}