Edited at

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

スクリーンショット 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点リーダー等の省略文字は出ない。


備考

(特になし)