查看NSLayoutManager
的文档,特别是drawUnderlineForGlyphRange:underlineType:baselineOffset:lineFragmentRect:lineFragmentGlyphRange:containerOrigin:方法,我注意到以下内容(强调我的):
underlineVal
画下划线的风格。此值是从NSUnderlineStyleAttributeName
的值派生的掩码 - 例如,(NSUnderlinePatternDash
|NSUnderlineStyleThick
)。 子类可以定义自定义下划线样式。
我的问题是:这到底意味着怎么做?
NSUnderlineStyle
是枚举,您无法扩展或覆盖。您当然可以为属性提供随机原始Int
值,但不包含在enum案例中:
self.addAttribute(NSUnderlineStyleAttributeName, value: 100022, range: lastUpdatedWordRange)
这将为布局管理器提供“无效”但可用的underlineType
:
但这并不安全,而且绝对不优雅。
我无法在线找到任何示例或在Apple文档中找到有关那些神话般的自定义下划线样式类型的更多线索。我很想知道我是否遗漏了一些明显的东西。
答案 0 :(得分:4)
我在这里有一个示例项目,我在TextKit上使用了一段时间,我回过头来完成了你正在寻找的内容:https://github.com/dtweston/text-kit-example
这种情况下的下划线是一条波浪线:
解决方案的核心是自定义NSLayoutManager:
let CustomUnderlineStyle = 0x11
class UnderlineLayoutManager: NSLayoutManager {
func drawFancyUnderlineForRect(_ rect: CGRect) {
let left = rect.minX
let bottom = rect.maxY
let width = rect.width
let path = UIBezierPath()
path.move(to: CGPoint(x: left, y: bottom))
var x = left
var y = bottom
var i = 0
while (x <= left + width) {
path.addLine(to: CGPoint(x: x, y: y))
x += 2
if i % 2 == 0 {
y = bottom + 2.0
}
else {
y = bottom
}
i += 1;
}
path.stroke()
}
override func drawUnderline(forGlyphRange glyphRange: NSRange, underlineType underlineVal: NSUnderlineStyle, baselineOffset: CGFloat, lineFragmentRect lineRect: CGRect, lineFragmentGlyphRange lineGlyphRange: NSRange, containerOrigin: CGPoint) {
if underlineVal.rawValue & CustomUnderlineStyle == CustomUnderlineStyle {
let charRange = characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
if let underlineColor = textStorage?.attribute(NSUnderlineColorAttributeName, at: charRange.location, effectiveRange: nil) as? UIColor {
underlineColor.setStroke()
}
if let container = textContainer(forGlyphAt: glyphRange.location, effectiveRange: nil) {
let boundingRect = self.boundingRect(forGlyphRange: glyphRange, in: container)
let offsetRect = boundingRect.offsetBy(dx: containerOrigin.x, dy: containerOrigin.y)
drawFancyUnderlineForRect(offsetRect)
}
}
else {
super.drawUnderline(forGlyphRange: glyphRange, underlineType: underlineVal, baselineOffset: baselineOffset, lineFragmentRect: lineRect, lineFragmentGlyphRange: lineGlyphRange, containerOrigin: containerOrigin)
}
}
}