我希望下划线与任何其他正常的下划线机制一样低于文本。然而,对于NSAttributed字符串,它会留下" g"#34;和" y'
例如:
它的外观:
如何增加下划线和标签之间的间距?
答案 0 :(得分:17)
无法使用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,搜索为“下划线”)。
答案 1 :(得分:4)
我添加了一条线(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]))
答案 2 :(得分:1)
您可以使用UITextView 我在NSLayoutManager中添加了自定义NSAttributedStringKey“ customUnderline”和令人毛骨悚然的方法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
}
}