web-dev-qa-db-ja.com

NSUnderlineStyleAttributeName下線の間隔

他の通常の下線メカニズムと同じように、下線をテキストの下に配置したいと思います。ただし、NSAttributed文字列を使用すると、「g」と「y」の穴が残ります。

例: enter image description here

どのように見えるべきか: enter image description here

下線とラベルの間隔を広げるにはどうすればよいですか?

23
MobileMon

NSAttributedStringまたはCoreTextを使用してその動作を制御する方法はありません(自分で下線を引くことを除いて)。 NSAttributedString そのためのオプションはありません(そして CoreTextにもオプションがありません )。

Appleシステムでは、最初のバージョン(ギャップあり)はAppleが提供し、システム全体で使用されるため、「予期される」動作です(およびSafari、TextEditなどのアプリ)。

本当に本当にギャップのない下線が必要な場合は、下線なしで文字列を描画し、自分で線を描画する必要があります(これは私が行う必要がありました 私のプロジェクトの1つで、難しいと言えます ;参照 このファイル 、「下線」を検索)。

21
DarkDust

高さ1、幅がラベルのような線(UIView)を、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]))
6
Ferran Maylinch

あなたは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
    }
}

そのように

2