Limit the number of lines for UITextview

IphoneCocoa TouchUitextview

Iphone Problem Overview


I was wondering how to limit the amount of LINES (not characters as asked in other questions) a user can enter when editing a UITextField.

Ideally, I would like to limit the input to max. 10 lines.

Where would I need to start? Do I do this with a method? In

 - (BOOL)textViewShouldBeginEditing:(UITextView *)aTextView

Iphone Solutions


Solution 1 - Iphone

Maciek Czarnik answer does not worked for me, but it got me insights what to do.

iOS 7+

Swift

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

ObjC

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

Solution 2 - Iphone

Maybe this can help (iOS 7+):

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

Even first line should do the trick I guess, but doesn't... Maybe its a bug in SDK

Solution 3 - Iphone

You have the right idea, but the wrong method. textView:shouldChangeTextInRange:replacementText: is called whenever the text is going to change; you can access the current content of the text view using its text property, and you can construct the new content from the passed range and replacement text with [textView.text stringByReplacingCharactersInRange:range withString:replacementText]. You can then count the number of lines and return YES to allow the change or NO to reject it.

Solution 4 - Iphone

in Swift 3.0 version:

self.textView.textContainer.maximumNumberOfLines = self.textViewNumberOflines
self.textView.textContainer.lineBreakMode = .byTruncatingTail

Solution 5 - Iphone

Maciek Czarnik's answer does not seem to work for me, even in iOS7. It gives me strange behavior, I don't know why.

What I do to limit the number of lines in the UITextView is simply :

(tested only in iOS7) In the following UITextViewDelegate method :

- (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];
    }
}

Solution 6 - Iphone

Here's a improved Version of Numereyes answer in Swift 4.2 / Swift 5

I made a little extension so I can reuse the code. I'm using a While-Loop to check if the size fits. This also works when the user pastes a lot of text at once.

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)
}        

Solution 7 - Iphone

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
}

And if you want to limit characters also:

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
    }
}

don't forget to add how many lines you want the limit to be in viewDidLoad:

txtView.textContainer.maximumNumberOfLines = 2

Solution 8 - Iphone

The other solutions given do not solve an issue related to a last line being created at the end (an 11th line in the question's case).

Here is a working solution with Swift 4.0 & Xcode 9.0 beta (found on this blog post)

   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: This solution does not handle the scenario where the last line is too long to display (text will be hidden on the far right side of the UITextView).

Solution 9 - Iphone

Similar to other answers, but usable directly from Storyboard and without subclassing:

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
        }
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionn.evermindView Question on Stackoverflow
Solution 1 - IphoneJamagasView Answer on Stackoverflow
Solution 2 - IphoneMaciek CzarnikView Answer on Stackoverflow
Solution 3 - IphoneAnomieView Answer on Stackoverflow
Solution 4 - Iphonelevin vargheseView Answer on Stackoverflow
Solution 5 - IphoneJon - LBABView Answer on Stackoverflow
Solution 6 - IphoneheyfrankView Answer on Stackoverflow
Solution 7 - IphoneBen ShabatView Answer on Stackoverflow
Solution 8 - IphonestandoussetView Answer on Stackoverflow
Solution 9 - IphoneAntziView Answer on Stackoverflow