How to limit the number of lines in UITextView?
You need to implement textView:shouldChangeTextInRange:replacementText:
. This method is called whenever the text is going to change. You can access the current content of the text view using its text property.
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.
EDIT: On OP request:
Swift:
func sizeOfString (string: String, constrainedToWidth width: Double, font: UIFont) -> CGSize {
return (string as NSString).boundingRectWithSize(CGSize(width: width, height: DBL_MAX),
options: NSStringDrawingOptions.UsesLineFragmentOrigin,
attributes: [NSFontAttributeName: font],
context: nil).size
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
let newText = (textView.text as NSString).stringByReplacingCharactersInRange(range, withString: text)
var textWidth = CGRectGetWidth(UIEdgeInsetsInsetRect(textView.frame, textView.textContainerInset))
textWidth -= 2.0 * textView.textContainer.lineFragmentPadding;
let boundingRect = sizeOfString(newText, constrainedToWidth: Double(textWidth), font: textView.font!)
let numberOfLines = boundingRect.height / textView.font!.lineHeight;
return numberOfLines <= 2;
}
Obj-C
- (CGSize) sizeOfString:(NSString*)str constrainedToWidth:(CGFloat)width andFont:(UIFont*)font
{
return [str boundingRectWithSize:CGSizeMake(width, DBL_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName: font}
context:nil].size;
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
NSString *newText = [textView.text stringByReplacingCharactersInRange:range withString:text];
CGFloat textWidth = CGRectGetWidth(UIEdgeInsetsInsetRect(textView.frame, textView.textContainerInset));
textWidth -= 2.0 * textView.textContainer.lineFragmentPadding;
CGSize boundingRect = [self sizeOfString:newText constrainedToWidth:textWidth andFont:textView.font];
int numberOfLines = boundingRect.height / textView.font.lineHeight;
return numberOfLines <= 2;
}
For iOS 7+
textView.textContainer.maximumNumberOfLines = 10;
textView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail;
OR
textView.textContainer.maximumNumberOfLines = 10;
[textView.layoutManager textContainerChangedGeometry:textView.textContainer];
In case anyone wants realtime line number count, this is how you do it with RxSwift & RxCocoa
let disposeBag = DisposeBag()
textView.rx_text.asDriver().driveNext { text in
let text = text as NSString
var textWidth: CGFloat = CGRectGetWidth(UIEdgeInsetsInsetRect(self.textView.frame, self.textView.textContainerInset))
textWidth -= 2.0 * self.textView.textContainer.lineFragmentPadding
let textAttributes = [NSFontAttributeName: UIFont(name: “Set your font here”, size: 26.0)!]
let boundingRect: CGRect = text.boundingRectWithSize(CGSizeMake(textWidth, 0), options: [NSStringDrawingOptions.UsesLineFragmentOrigin, NSStringDrawingOptions.UsesFontLeading], attributes: textAttributes, context: nil)
let font = UIFont(name: “Set your font here”, size: 26.0)
guard let lineHeight = font?.lineHeight else {return}
let numberOfLines = CGRectGetHeight(boundingRect) / lineHeight
if numberOfLines <= 2 {
self.textView.typingAttributes = [NSFontAttributeName: UIFont(name: “Set your font here”, size: 26.0)!, NSForegroundColorAttributeName: “Set your color here”]
} else {
self.textView.typingAttributes = [NSFontAttributeName: UIFont(name: “Set your font here”, size: 18.0)!, NSForegroundColorAttributeName: “Set your color here”]
}
}.addDisposableTo(disposeBag)