Limit the number of lines for UITextview
IphoneCocoa TouchUitextviewIphone 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
}
}
}