iOS
Swift

日本語で行間をいじった時のバグ

More than 1 year has passed since last update.

文字列表示で行間をいじる時、NSAttributedString,NSParagraphStyleを使うと思います。

let style = NSMutableParagraphStyle()
style.lineSpacing = 10.0 // 行間設定

let attr = [NSParagraphStyleAttributeName: style, NSFontAttributeName: font]
let attrStr = NSMutableAttributedString(string: text, attributes: attr)
label.attributedText = attrStr

ただ日本語で1行のみの表示の時、下に本来は入るはずのない行間のスペースが入ってしまいます。
(原因はわかりませんでした。。)
複数行やアルファベットの時は想定通りになります。

スクリーンショット 2017-01-06 11.05.26.png

対策

1行の場合かつ高さがフォントの高さ+行間スペースの場合のみ高さをフォントの高さに合わせるサブクラスを実装しました。

class LineSpacingLabel: UILabel {
    var adjustedHeight = CGFloat(0.0)
    var adjustedForLineSpacingBug = false

    override func drawText(in rect: CGRect) {
        var baseRect = rect

        // 本来の描画されるスペースを小さくしているため、そのままにすると上下が切れてしまうのでここで調整する
        if adjustedForLineSpacingBug {
            baseRect.origin.y = adjustedHeight / 2.0
        }

        super.drawText(in: baseRect)
    }

    override var intrinsicContentSize: CGSize {
        let baseSize = super.intrinsicContentSize

        adjustedHeight = 0.0
        adjustedForLineSpacingBug = false

        // lineSpacingを設定しているかつlineSpacingが0以外の時のみ処理を行う
        if let paragraphStyle = attributedText?.attribute(NSParagraphStyleAttributeName, at: 0, effectiveRange: nil) as? NSParagraphStyle,
            paragraphStyle.lineSpacing != 0.0 {

            let fontHeight = ceil(self.font.lineHeight)
            let lineSpacing = paragraphStyle.lineSpacing

            // 固有サイズの高さがフォントの高さ+行間スペースの場合(1行だけど下に行間スペースが挿入されている場合)
            // 本来の高さに変更する
            if baseSize.height == fontHeight + lineSpacing {
                adjustedHeight = lineSpacing
                adjustedForLineSpacingBug = true
                return CGSize(width: baseSize.width, height: fontHeight)
            }
        }

        return baseSize
    }
}

スクリーンショット 2017-01-06 11.30.57.png

これで日本語1行の時でも変なスペースが開かなくなりました。
TextKitとか使ってもうちょっと厳密にやってもいい気はしますが、今回は問題なかったので一旦放置。

その他

もっといい実装ありましたらコメントいただければと思います!

それとNSAttributedStringを扱う時はこれ使うとめっちゃ楽!
https://github.com/delba/TextAttributes

この実装のサンプルは以下になります。
https://github.com/furuyan/LineSpacingSapmle