web-dev-qa-db-ja.com

UITextViewの行数をカウントし、フレームサイズでラップされた行

テキストビューのフレームによってテキストがラップされるとき、テキストがラップされているかどうかを識別するための区切り文字があるかどうかを知りたいと思いました。

たとえば、テキストビューの幅が50ピクセルで、テキストがそれを超える場合、テキストが次の行に折り返されます。

テキストビューの行数を数えたかったのですが。現在、「\ n」と「\ r」は私を助けていません。

私のコードは:

NSCharacterSet *aCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"\n\r"];
    NSArray *myArray = [textViewText componentsSeparatedByCharactersInSet:aCharacterSet];
    NSLog(@"%d",[myArray count]);
25
Abhinav

このバリエーションは、行の折り返し方法とUITextViewの最大サイズを考慮しており、より正確な高さを出力する場合があります。たとえば、テキストが収まらない場合は表示サイズに切り捨てられ、単語全体をラップすると(デフォルト)、それ以外の場合よりも行数が増える可能性があります。

UIFont *font = [UIFont boldSystemFontOfSize:11.0];
CGSize size = [string sizeWithFont:font 
                      constrainedToSize:myUITextView.frame.size 
                      lineBreakMode:UILineBreakModeWordWrap]; // default mode
float numberOfLines = size.height / font.lineHeight;
28
Jano

lineHeightプロパティとフォントlineHeightを使用する必要があります。

Objective-C

int numLines = txtview.contentSize.height / txtview.font.lineHeight;

スウィフト

let numLines = (txtview.contentSize.height / txtview.font.lineHeight) as? Int

正しい行数を取得しています。それがあなたにも役立つことを願っています。

27
Himanshu padia

スウィフト3:

let layoutManager:NSLayoutManager = textView.layoutManager
let numberOfGlyphs = layoutManager.numberOfGlyphs
var numberOfLines = 0
var index = 0
var lineRange:NSRange = NSRange()

while (index < numberOfGlyphs) {
    layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
    index = NSMaxRange(lineRange);
    numberOfLines = numberOfLines + 1
}

print(numberOfLines)
15
castillejoale

Swift拡張:

@himanshu padia回答の使用

_//MARK: - UITextView
extension UITextView{

    func numberOfLines() -> Int{
        if let fontUnwrapped = self.font{
            return Int(self.contentSize.height / fontUnwrapped.lineHeight)
        }
        return 0
    }

}
_

使用法:yourTextView.numberOfLines()

何らかの理由でテキストビューのフォントがnilの場合、戻り値はゼロになることに注意してください。

11
Juan Boero

私はこの問題の完璧な解決策をAppleの Text Layout Programming Guide で見つけました。 Appleが提供するソリューションは次のとおりです。

NSLayoutManager *layoutManager = [textView layoutManager];
unsigned numberOfLines, index;
unsigned numberOfGlyphs = [layoutManager numberOfGlyphs];
NSRange lineRange;

for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){
    (void) [layoutManager lineFragmentRectForGlyphAtIndex:index effectiveRange:&lineRange];
    index = NSMaxRange(lineRange);
}

これは、UITextViewの拡張機能に簡単に書き込むことも、UITextViewオブジェクトをパラメーターとして受け取るスタンドアロンメソッドとして書き込むこともできます。

10
Luke Chase

Swift 4バージョンのLuke Chase'sanswer

let numberOfGlyphs = textView.layoutManager.numberOfGlyphs
var index = 0, numberOfLines = 0
var lineRange = NSRange(location: NSNotFound, length: 0)

while index < numberOfGlyphs {
  textView.layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
  index = NSMaxRange(lineRange)
  numberOfLines += 1
}
8
Yeesha

TextView.textContainerInsetを考慮する必要があります。また、行番号は間違いなく整数であるため、計算された値を丸める必要があります。

float rawLineNumber = (textView.contentSize.height - textView.textContainerInset.top - textView.textContainerInset.bottom) / textView.font.lineHeight;
int finalLineNumber = round(rawLineNumber)

実際の場合、次の結果が表示されることがありますrawLineNumber = 3.008099 finalLineNumber = 3(3行)

7
Jacky
   func numberOfLines(textView: UITextView) -> Int {
    let layoutManager = textView.layoutManager
    let numberOfGlyphs = layoutManager.numberOfGlyphs
    var lineRange: NSRange = NSMakeRange(0, 1)
    var index = 0
    var numberOfLines = 0

    while index < numberOfGlyphs {
        layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
        index = NSMaxRange(lineRange)
        numberOfLines += 1
    }
    return numberOfLines
}

私のためにうまくいく

4
Softlabsindia

これを使用します(_text_vはテキストビューです):

-(NSInteger) linesCount {
    return _text_v.contentSize.height/_text_v.font.lineHeight;
}
4
Max

入力TextView(チャットのテキスト入力ボックスなど)の行数をリアルタイムで計算することに多くの時間を費やしましたが、そのような場合に機能する唯一の解決策は、Luke Chaseの(何らかの理由でframe.heightアプローチ)です。 textViewに入力された3番目または4番目の文字のみが更新されるように見えるため、正確ではありません)。

ただし、コメンターが述べたように、ユーザーが生成した改行(「\ n」またはキーボードのリターンプレス)が適切に考慮されないという小さなバグがあります。さらに奇妙なことに、そのような最初の改行が「欠落」しているだけで、後続の改行は正しくキャプチャされます(4回行に移動すると、最初の改行がないことが3行だけ返されます)。

したがって、そのバグを回避するために、最初の改行(文字「\ n」)を記録して、gliphメソッドが返す行数に手動で行を追加します。

与えるコードでは:

    func offSetTableViewIfNeeded() {
    let numberOfGlyphs = textView.layoutManager.numberOfGlyphs
    var index : Int = 0
    var lineRange = NSRange(location: NSNotFound, length: 0)
    var currentNumOfLines : Int = 0
    var numberOfParagraphJump : Int = 0

    while index < numberOfGlyphs {
        textView.layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
        index = NSMaxRange(lineRange)
        currentNumOfLines += 1

        // Observing whether user went to line and if it's the first such line break, accounting for it.
        if textView.text.last == "\n", numberOfParagraphJump == 0 {
            numberOfParagraphJump = 1
        }
    }

    currentNumOfLines += numberOfParagraphJump

    print("Number of lines is:", currentNumOfLines)

これが入力textViewの非常に奇妙な動作に苦労している他の人に役立つことを願っています(なぜAppleが箱から出して#of lineメソッドを提供しないのか理解できません!))。

3
Franc

Swift 5.0

extension UITextView {
    func sizeFit(width: CGFloat) -> CGSize {
        let fixedWidth = width
        let newSize = sizeThatFits(CGSize(width: fixedWidth, height: .greatestFiniteMagnitude))
        return CGSize(width: fixedWidth, height: newSize.height)
    }

    func numberOfLine() -> Int {
        let size = self.sizeFit(width: self.bounds.width)
        let numLines = Int(size.height / (self.font?.lineHeight ?? 1.0))
        return numLines
    }
}
1
Tà Truhoada

改善および更新Luke Chase'sanswer to Swift 5、XCode 11、iOS 13 toテキストビューの行数を取得し、テーブルビューのセルの高さを自動サイズ変更します。

1)セルの高さが静的なストーリーボードを使用して、好きなようにデザインできます。

2)viewDidLoadで、推定行の高さとtextViewデリゲートを追加します。

override func viewDidLoad() {
        super.viewDidLoad()

        quoteTextView.delegate = self
        tableView.estimatedRowHeight = 142

    }

3)heightForRowAtのテーブルビューデリゲートを追加します。

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}

4)UITextViewDelegateに準拠し、ユーザーがテキストを入力するときにリッスンします。

extension ViewController: UITextViewDelegate {

    func textViewDidChange(_ textView: UITextView) {
        // Refresh tableView cell
        if textView.numberOfLines > 2 { // textView in storyboard has two lines, so we match the design
            // Animated height update
            DispatchQueue.main.async {
                self.tableView?.beginUpdates()
                self.tableView?.endUpdates()
            }
        }
    }
}

5)UITextView拡張機能を追加して、冗長なコードを回避し、アプリ全体で使用できるようにします。

extension UITextView {
    var numberOfLines: Int {
        // Get number of lines
        let numberOfGlyphs = self.layoutManager.numberOfGlyphs
        var index = 0, numberOfLines = 0
        var lineRange = NSRange(location: NSNotFound, length: 0)

        while index < numberOfGlyphs {
            self.layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
          index = NSMaxRange(lineRange)
          numberOfLines += 1
        }

        return numberOfLines
    }
}

プレビュー

enter image description here

1
Doci

NSLayoutManagerを使用してみてください:

NSLayoutManager *layoutManager = [textView layoutManager];
unsigned numberOfLines, index, numberOfGlyphs =
        [layoutManager numberOfGlyphs];
NSRange lineRange;
for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){
    (void) [layoutManager lineFragmentRectForGlyphAtIndex:index
            effectiveRange:&lineRange];
    index = NSMaxRange(lineRange);
}

参照

CountLines

0
MichaelMao