Help us understand the problem. What is going on with this article?

[Tips] UITextViewの行数などを取得する

More than 1 year has passed since last update.

スクリーンショット 2019-03-05 16.34.16.png

概要

UITextviewの表示状況取得のためのTips。ちなみに設定系は、[Tips] UITextviewをUILabelっぽく使うで。

0,取得タイミングについて

同じtextでも表示領域がちがえば行数も変わってくるわけで、例えば表示前に情報を取得しようと思ってもうまくいかないことが多い。AutoLayoutがベースになっているなら、UIViewControllerライフサイクルのviewWillLayoutSubViewsタイミングで取得するのをオススメ。

1,行数を取得する

UITextViewの行数を取得する
extension UITextView {
    var numberOfLines: Int {
        // prepare
        var computingLineIndex = 0
        var computingGlyphIndex = 0
        // compute
        while computingGlyphIndex < layoutManager.numberOfGlyphs {
            var lineRange = NSRange()
            layoutManager.lineFragmentRect(forGlyphAt: computingGlyphIndex, effectiveRange: &lineRange)
            computingGlyphIndex = NSMaxRange(lineRange)
            computingLineIndex += 1
        }
        // return
        if textContainer.maximumNumberOfLines > 0 {
            return min(textContainer.maximumNumberOfLines, computingLineIndex)
        } else {
            return computingLineIndex
        }
    }
}

Apple公式ドキュメントに方法(Objective-C!!!)がでていたのでこれを参考に実装。ただし、素直に実装してみると、「最大行数設定しているのに改行の入り方によってはそれよりも大きい数が返ってきてしまう」という不具合があったため、上記のように返り値に少し工夫をした。

参考:Apple: Text Layout Programming Guide: Counting Lines of Text

2,表示省略されているかを取得する

UITextViewの表示省略されているか
extension UITextView {
    var isTruncated: Bool {
        // prepare
        let lineMax = textContainer.maximumNumberOfLines > 0 ? textContainer.maximumNumberOfLines : Int.max
        var computingLineIndex = 0
        var computingGlyphIndex = 0
        // compute & return
        while computingGlyphIndex < layoutManager.numberOfGlyphs {
            // analyze line
            let overLineMax = computingLineIndex > lineMax
            let existsTruncatedGlyph: Bool = {
                let truncatedGlyphRange = layoutManager.truncatedGlyphRange(inLineFragmentForGlyphAt: computingGlyphIndex)
                return truncatedGlyphRange.location != NSNotFound
            }()
            // guard
            guard !overLineMax, !existsTruncatedGlyph else {
                return true
            }
            // prepare for next
            var lineRange = NSRange()
            self.layoutManager.lineFragmentRect(forGlyphAt: computingGlyphIndex, effectiveRange: &lineRange)
            computingGlyphIndex = NSMaxRange(lineRange)
            computingLineIndex += 1
        }
        return false
    }
}

1の応用。指定行の省略文字位置を取得するファンクションtruncatedGlyphRange(inLineFragmentForGlyphAt:)がlayoutManagerにあるためこれを用いる。

ちなみに、設定系の話だが、.textContainer.lineBreakMode = .byTruncatingTailとかの設定をしないと3点リーダー等の省略文字は出ない。

備考

(特になし)

ShoichiKuraoka
車メーカー←フリーランス←SIer。フリーランスの頃はiOS系を主に担当。参加プロジェクト:業務用プレゼンApp、位置情報SDK、通貨App、動画配信App…etc
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away