我想自定义NSSAttributedString的下划线样式,因为patternDot实际上只是短破折号,我想要真正的圆点(例如,参见Customize underline pattern in NSAttributedString (iOS7+))。
我取得了相当远,但我有一个不一致的对齐问题与结果的下划线,显然是因为自定义行距。如果下划线不是在文本的最后一行,那么下划线就太低了,但是如果是在最后一行文本上,下划线就太高了。大概是因为最后一行没有任何行距。我想,如果有一种方法,在我的drawUnderline中找出我开的是哪一行文字,我可以相应地调整y偏移量,但还有更简单的方法吗?
示例:

class DotUnderlineLayoutManager: NSLayoutManager {
let color: UIColor
init(color: UIColor = .black) {
self.swatch = swatch
super.init()
}
override func drawUnderline(forGlyphRange glyphRange: NSRange, underlineType _: NSUnderlineStyle, baselineOffset: CGFloat, lineFragmentRect: CGRect, lineFragmentGlyphRange _: NSRange, containerOrigin: CGPoint) {
guard let container = textContainer(
forGlyphAt: glyphRange.location,
effectiveRange: nil
) else { return }
let rect = boundingRect(forGlyphRange: glyphRange, in: container)
let offsetRect = rect.offsetBy(
dx: containerOrigin.x,
dy: containerOrigin.y // + baselineOffset <- adding this helps a bit with an underline on the last line but messes up other lines even more
)
let path = UIBezierPath()
path.strokeDottedLine(under: offsetRect, color: color)
}
}
private extension UIBezierPath {
func strokeDottedLine(under rect: CGRect, color: UIColor) {
lineWidth = 2
lineCapStyle = .round
setLineDash([0.1, 5], count: 2, phase: 0)
move(to: .init(x: rect.minX, y: rect.maxY))
addLine(to: .init(x: rect.maxX, y: rect.maxY))
color.setStroke()
stroke()
}
}
extension NSUnderlineStyle {
static var patternCircularDot: NSUnderlineStyle {
NSUnderlineStyle(rawValue: 0x11)
}
}let textView: UITextView = {
let layout = DotUnderlineLayoutManager(swatch: swatch)
let storage = NSTextStorage()
storage.addLayoutManager(layout)
let initialSize = CGSize(width: 0, height: CGFloat.greatestFiniteMagnitude)
let container = NSTextContainer(size: initialSize)
container.widthTracksTextView = true
layout.addTextContainer(container)
let textView = UITextView(frame: .zero, textContainer: container)
textView.isUserInteractionEnabled = false
textView.isEditable = false
textView.isScrollEnabled = false
textView.backgroundColor = .clear
return textView
}()
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byWordWrapping
paragraphStyle.lineSpacing = 10 // any non-zero value
textView.attributedText = NSAttributedString(
string: myText,
attributes: [
.paragraphStyle: paragraphStyle
.underlineStyle: NSUnderlineStyle.patternCircularDot.union(.single).rawValue
]
)编辑:自定义字体实际上并不重要,只是段落样式上的行距,相应地简化了问题:

发布于 2022-06-16 21:46:53
与其通过NSMutableParagraphStyle设置行距,不如使用NSLayoutManagerDelegate的layoutManager(lineSpacingAfterGlyphAt…)。
func layoutManager(_ layoutManager: NSLayoutManager, lineSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat {
// set line spacing as needed here
}我甚至将我的NSLayoutManager子类设置为它自己的委托。疯了,我知道。
然后,我调整了我的直角偏移,减去一半的行距,这似乎很好。
https://stackoverflow.com/questions/72651292
复制相似问题