首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何用UITextView调整自定义下划线的垂直位置,用非零lineSpacing NSParagraphStyle调整NSLayoutManager?

如何用UITextView调整自定义下划线的垂直位置,用非零lineSpacing NSParagraphStyle调整NSLayoutManager?
EN

Stack Overflow用户
提问于 2022-06-16 19:59:22
回答 1查看 87关注 0票数 0

我想自定义NSSAttributedString的下划线样式,因为patternDot实际上只是短破折号,我想要真正的圆点(例如,参见Customize underline pattern in NSAttributedString (iOS7+))。

我取得了相当远,但我有一个不一致的对齐问题与结果的下划线,显然是因为自定义行距。如果下划线不是在文本的最后一行,那么下划线就太低了,但是如果是在最后一行文本上,下划线就太高了。大概是因为最后一行没有任何行距。我想,如果有一种方法,在我的drawUnderline中找出我开的是哪一行文字,我可以相应地调整y偏移量,但还有更简单的方法吗?

示例:

代码语言:javascript
复制
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)
  }
}
代码语言:javascript
复制
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
    ]
)

编辑:自定义字体实际上并不重要,只是段落样式上的行距,相应地简化了问题:

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-06-16 21:46:53

与其通过NSMutableParagraphStyle设置行距,不如使用NSLayoutManagerDelegatelayoutManager(lineSpacingAfterGlyphAt…)

代码语言:javascript
复制
func layoutManager(_ layoutManager: NSLayoutManager, lineSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat {
    // set line spacing as needed here
}

我甚至将我的NSLayoutManager子类设置为它自己的委托。疯了,我知道。

然后,我调整了我的直角偏移,减去一半的行距,这似乎很好。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72651292

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档