【问题标题】:NSUnderlineStyleAttributeName Underline spacingNSUnderlineStyleAttributeName 下划线间距
【发布时间】:2014-12-30 18:22:26
【问题描述】:

我希望下划线像任何其他正常的下划线机制一样位于文本下方。但是,对于 NSAttributed 字符串,它会留下带有“g's”和“y's”的孔

示例:

应该是什么样子:

如何增加下划线和标签之间的间距?

【问题讨论】:

  • 据我所知你不能,但即便如此:为什么要第二个版本?它的可读性要差得多。尤其是在小字体时。
  • @DarkDust 我为什么要它?因为这是预期的下划线行为。打开 Microsoft Word 或 Google 文档,这就是你得到的!

标签: ios nsattributedstring underline


【解决方案1】:

你可以使用 UITextView 我在 NSLayoutManager 中添加了自定义 NSAttributedStringKey "customUnderline" 和 swizzling 方法 drawUnderline。

import Foundation
import SwiftyAttributes
import UIKit

private let swizzling: (AnyClass, Selector, Selector) -> Void = { forClass, originalSelector, swizzledSelector in
    guard let originalMethod = class_getInstanceMethod(forClass, originalSelector),
        let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) else {
            return
    }
    method_exchangeImplementations(originalMethod, swizzledMethod)
}

extension NSAttributedStringKey {
    static var customUnderline: NSAttributedStringKey = NSAttributedStringKey("customUnderline")
}

extension Attribute {
    static var customUnderline: Attribute = Attribute.custom(NSAttributedStringKey.customUnderline.rawValue, true)
}

extension NSLayoutManager {

    // MARK: - Properties

    static let initSwizzling: Void = {
        let originalSelector = #selector(drawUnderline(forGlyphRange:underlineType:baselineOffset:lineFragmentRect:lineFragmentGlyphRange:containerOrigin:))
        let swizzledSelector = #selector(swizzled_drawUnderline(forGlyphRange:underlineType:baselineOffset:lineFragmentRect:lineFragmentGlyphRange:containerOrigin:))
        swizzling(NSLayoutManager.self, originalSelector, swizzledSelector)
    }()

    // MARK: - Functions

    @objc
    func swizzled_drawUnderline(forGlyphRange glyphRange: NSRange, underlineType underlineVal: NSUnderlineStyle, baselineOffset: CGFloat, lineFragmentRect lineRect: CGRect, lineFragmentGlyphRange lineGlyphRange: NSRange, containerOrigin: CGPoint) {
        guard needCustomizeUnderline(underlineType: underlineVal) else {
            swizzled_drawUnderline(forGlyphRange: glyphRange,
                                   underlineType: underlineVal,
                                   baselineOffset: baselineOffset,
                                   lineFragmentRect: lineRect,
                                   lineFragmentGlyphRange: lineGlyphRange,
                                   containerOrigin: containerOrigin)
            return
        }

        let heightOffset = containerOrigin.y - 1 + (getFontHeight(in: glyphRange) ?? (lineRect.height / 2))
        drawStrikethrough(forGlyphRange: glyphRange,
                          strikethroughType: underlineVal,
                          baselineOffset: baselineOffset,
                          lineFragmentRect: lineRect,
                          lineFragmentGlyphRange: lineGlyphRange,
                          containerOrigin: CGPoint(x: containerOrigin.x,
                                                   y: heightOffset))
    }

    // MARK: - Private functions

    private func needCustomizeUnderline(underlineType underlineVal: NSUnderlineStyle) -> Bool {
        guard underlineVal == NSUnderlineStyle.styleSingle else {
            return false
        }
        let attributes = textStorage?.attributes(at: 0, effectiveRange: nil)
        guard let isCustomUnderline = attributes?.keys.contains(.customUnderline), isCustomUnderline else {
            return false
        }
        return true
    }

    private func getFontHeight(in glyphRange: NSRange) -> CGFloat? {
        let location = characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil).location
        guard let font = textStorage?.attribute(.font, at: location, effectiveRange: nil) as? UIFont else {
            return nil
        }
        return font.capHeight
    }
}

like that

【讨论】:

  • 我刚刚提交了改进建议 #47574345
  • 文本上的下划线使带下划线的数字很难阅读。似乎像 TextEdit 这样的编辑器以自己的方式处理它,像素间距增加了 1 个。
【解决方案2】:

没有办法用NSAttributedString 或CoreText 来控制这种行为(除了自己画下划线)。 NSAttributedString 对此没有选择(和CoreText hasn't got one, either)。

在 Apple 系统上,第一个版本(有差距)是“预期的”行为,因为它是 Apple 提供并在整个系统(以及 Safari、TextEdit 等应用程序)中使用的行为。

如果你真的,真的想有没有间隙的下划线,你需要画没有下划线的字符串,然后自己画线(我需要这样做in one of my projects and I can tell you it's hard;参见this file,搜索“下划线” )。

【讨论】:

  • 说起来并不难 ;)
【解决方案3】:

我添加了一条线(UIView),高度为 1,宽度类似于标签,与 UILabel 的底部对齐。

    let label = UILabel()
    label.text = "underlined text"

    let spacing = 2 // will be added as negative bottom margin for more spacing between label and line

    let line = UIView()
    line.translatesAutoresizingMaskIntoConstraints = false
    line.backgroundColor = label.textColor
    label.addSubview(line)
    label.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[line]|", metrics: nil, views: ["line":line]))
    label.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[line(1)]-(\(-spacing))-|", metrics: nil, views: ["line":line]))

【讨论】:

  • 其实是个好主意。如果您将线视图与标签的 baseline 对齐,它可能会更好地工作(不幸的是,您不能使用视觉格式这样做)。
  • 是的,视觉格式有点受限。这让我想起了我构建了一个LayoutBuilder 实用程序类,它接受扩展约束(用于中心、基线等)。
猜你喜欢
  • 2014-01-01
  • 2013-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-30
  • 2019-09-07
  • 2010-12-16
相关资源
最近更新 更多