I have a uitextview
subview
in an inputContainerView as it may appear in a messaging app.
Programmatically, on load, the height anchor of the uitextview
is set when the inputContainerView is initialized. This constraint works great in increasing the container height as more lines of text are typed.
My goal is to cap the height of the uitextview
after reaching X number of lines where any lines typed thereafter are scrollable. And likewise, when the number of lines falls below the max, it returns to its original form - not scrollable and auto-sizing height based on content.
After multiple trials and research, I've managed to get the height to fixate once hitting the max number of lines, but I cannot seem to figure out how to return it to its original form once the number of lines have fallen below the max. It appears the uitextview
height stays stuck at the height the number of lines have maxed on.
Below is relevant code:
//Container View Initialization, onLoad Frame Height is set to 60
override init(frame: CGRect) {
super.init(frame: frame)
autoresizingMask = .flexibleHeight
addSubview(chatTextView)
textView.heightAnchor.constraint(greaterThanOrEqualToConstant: frame.height - 20).isActive = true
}
//TextView Delegate
var isTextViewOverMaxHeight = false
var textViewMaxHeight: CGFloat = 0.0
func textViewDidChange(_ textView: UITextView) {
let numberOfLines = textView.contentSize.height/(textView.font?.lineHeight)!
if Int(numberOfLines) > 5 {
if !isTextViewOverMaxHeight {
containerView.textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 40).isActive = false
containerView.textView.heightAnchor.constraint(equalToConstant:
containerView.textView.frame.height).isActive = true
containerView.textView.isScrollEnabled = true
textViewMaxHeight = containerView.textView.frame.height
isTextViewOverMaxHeight = true
}
} else {
if isTextViewOverMaxHeight {
containerView.textView.heightAnchor.constraint(equalToConstant: textViewMaxHeight).isActive = false
containerView.textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 40).isActive = true
containerView.textView.isScrollEnabled = false
isTextViewOverMaxHeight = false
}
}
}
You have overcomplicated a simple problem at hand, here is how can you achieve what you want
class ViewController: UIViewController {
var heightConstraint: NSLayoutConstraint!
var heightFor5Lines: CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
let textView = UITextView(frame: CGRect.zero)
textView.delegate = self
textView.backgroundColor = UIColor.red
textView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(textView)
textView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
textView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
textView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
heightConstraint = textView.heightAnchor.constraint(equalToConstant: 30.0)
heightConstraint.isActive = true
}
}
extension ViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
let numberOfLines = textView.contentSize.height/(textView.font?.lineHeight)!
if Int(numberOfLines) > 5 {
self.heightConstraint.constant = heightFor5Lines
} else {
if Int(numberOfLines) == 5 {
self.heightFor5Lines = textView.contentSize.height
}
self.heightConstraint.constant = textView.contentSize.height
}
textView.layoutIfNeeded()
}
}
How this works?:
Its simple, your textView starts off with some height (height constraint is necessary here because I haven't neither disabled its scroll nor have I provided bottom constraint, so it does not have enough data to evaluate its intrinsic height) and every time text changes you check for number of lines, as long as number of lines is less than threshold number of lines you keep increasing the height constraint of your textView so that its frame matches the contentSize (hence no scrolling) and once it hits the expected number of lines, you restrict height, so that textView frame is less than the actual content size, hence scrolls automatically
Hope this helps