web-dev-qa-db-ja.com

UITextviewの行数を制限する

UITextFieldを編集するときにユーザーが入力できるLINESの量(他の質問で尋ねられた文字ではない)を制限する方法を考えていました。

理想的には、入力を最大に制限したいと思います。 10行。

どこから始める必要がありますか?メソッドでこれを行いますか?に

 - (BOOL)textViewShouldBeginEditing:(UITextView *)aTextView
65
n.evermind

あなたは正しい考えを持っていますが、間違った方法を持っています。 textView:shouldChangeTextInRange:replacementText:は、テキストが変更されるたびに呼び出されます。 textプロパティを使用してテキストビューの現在のコンテンツにアクセスできます。また、渡された範囲から新しいコンテンツを作成し、[textView.text stringByReplacingCharactersInRange:range withString:replacementText]で置換テキストを作成できます。その後、行数をカウントし、YESを返して変更を許可するか、NOを返して拒否します。

15
Anomie

Maciek Czarnikの答えは私には役に立たなかったが、どうすればよいか洞察を得た。

iOS 7以降

Swift

textView.textContainer.maximumNumberOfLines = 10
textView.textContainer.lineBreakMode = .byTruncatingTail

ObjC

textView.textContainer.maximumNumberOfLines = 10;
textView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail;
204
Jamagas

たぶんこれが役立つ可能性があります(iOS 7以降):

textView.textContainer.maximumNumberOfLines = 10;
[textView.layoutManager textContainerChangedGeometry:textView.textContainer];

最初の行でも私が推測するトリックを行う必要がありますが、そうではありません...おそらくSDKのバグ

52
Maciek Czarnik

Maciek Czarnikの答えは、iOS7でさえもうまくいかないようです。それは私に奇妙な振る舞いを与えます、理由はわかりません。

UITextViewの行数を制限するために私がすることは単純です:

(iOS7でのみテスト済み)次のUITextViewDelegateメソッドで:

- (void)textViewDidChange:(UITextView *)textView
{
    NSUInteger maxNumberOfLines = 5;
    NSUInteger numLines = textView.contentSize.height/textView.font.lineHeight;
    if (numLines > maxNumberOfLines)
    {
        textView.text = [textView.text substringToIndex:textView.text.length - 1];
    }
}
12
Numereyes

Swift 3.0 バージョン:

self.textView.textContainer.maximumNumberOfLines = self.textViewNumberOflines
self.textView.textContainer.lineBreakMode = .byTruncatingTail
11
levin varghese

Swift 4.2/Swift 5のNumereyes回答の改善されたバージョンを次に示します。

コードを再利用できるように、少し拡張しました。 While-Loopを使用して、サイズが合っているかどうかを確認しています。これは、ユーザーが一度に多くのテキストを貼り付けるときにも機能します。

extension UITextView {        
    var numberOfCurrentlyDisplayedLines: Int {
        let size = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        //for Swift <=4.0, replace with next line:
        //let size = systemLayoutSizeFitting(UILayoutFittingCompressedSize)

        return Int(((size.height - layoutMargins.top - layoutMargins.bottom) / font!.lineHeight))
    }

    /// Removes last characters until the given max. number of lines is reached
    func removeTextUntilSatisfying(maxNumberOfLines: Int) {
        while numberOfCurrentlyDisplayedLines > (maxNumberOfLines) {
            text = String(text.dropLast())
            layoutIfNeeded()
        }
    }
}

// Use it in UITextView's delegate method:
func textViewDidChange(_ textView: UITextView) {        
    textView.removeTextUntilSatisfying(maxNumberOfLines: 10)
}        
5
fl034

指定された他のソリューションでは、最後に作成される最後の行(質問の場合は11行目)に関連する問題は解決されません。

Swift 4.0およびXcode 9.0ベータ版( このブログ投稿 にあります)

   class ViewController: UIViewController, UITextViewDelegate {

      @IBOutlet weak var textView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()
        textView.delegate = self
        textView.textContainer.maximumNumberOfLines = 10
        textView.textContainer.lineBreakMode = .byWordWrapping
      }

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

        let existingLines = textView.text.components(separatedBy: CharacterSet.newlines)
        let newLines = text.components(separatedBy: CharacterSet.newlines)
        let linesAfterChange = existingLines.count + newLines.count - 1

        return linesAfterChange <= textView.textContainer.maximumNumberOfLines
    }

Nota bene:このソリューションは、最後の行が長すぎて表示できないシナリオを処理しません(テキストはUITextViewの右端に隠れます) )。

2
standousset

他の回答に似ていますが、ストーリーボードから直接、サブクラス化せずに使用できます:

extension UITextView {
    @IBInspectable var maxNumberOfLines: NSInteger {
        set {
            textContainer.maximumNumberOfLines = maxNumberOfLines
        }
        get {
            return textContainer.maximumNumberOfLines
        }
    }
    @IBInspectable var lineBreakByTruncatingTail: Bool {
        set {
            if lineBreakByTruncatingTail {
                textContainer.lineBreakMode = .byTruncatingTail
            }
        }
        get {
            return textContainer.lineBreakMode == .byTruncatingTail
        }
    }
}
1
Antzi

for Swift 4これはバグなしで機能しました:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    let existingLines = textView.text.components(separatedBy: CharacterSet.newlines)
    let newLines = text.components(separatedBy: CharacterSet.newlines)
    let linesAfterChange = existingLines.count + newLines.count - 1
    return linesAfterChange <= textView.textContainer.maximumNumberOfLines
}

また、文字を制限したい場合:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        let existingLines = textView.text.components(separatedBy: CharacterSet.newlines)
        let newLines = text.components(separatedBy: CharacterSet.newlines)
        let linesAfterChange = existingLines.count + newLines.count - 1
        if(text == "\n") {
            return linesAfterChange <= textView.textContainer.maximumNumberOfLines
        }

        let newText = (textView.text as NSString).replacingCharacters(in: range, with: text)
        let numberOfChars = newText.count
        return numberOfChars <= 30 // 30 characters limit
    }
}

viewDidLoadに制限する行数を追加することを忘れないでください:

txtView.textContainer.maximumNumberOfLines = 2
0
Ben Shabat