I have this UITextView & I want it's height to change dynamically when the user is typing on it. I want to do it programmatically. I have the UITextView above another UIView. The constraints are set as below:
addtextview.leadingAnchor.constraint(equalTo: addtasktextview.leadingAnchor, constant: 8).isActive = true
addtextview.trailingAnchor.constraint(equalTo: addtasktextview.trailingAnchor, constant: -8).isActive = true
addtextview.topAnchor.constraint(equalTo: addtasktextview.topAnchor, constant: 8).isActive = true
addtextview.bottomAnchor.constraint(equalTo: addtasktextview.bottomAnchor, constant: -40).isActive = true
Interestingly enough, I am using textview in tableViewCells as well and dynamically changing the height by using just this constraint method, but over here it is not working.
I want the textview's height to increase in such a way that it moves upward. So when a new line starts, the top part should move maintaining the spacing below.
How can I do it? Help will be appreciated it.
UPDATE: I was able to get it working with @upholder-of-truth 's answer below. I was also able to dynamically change the parent UIView container height by finding the difference between the textview normal height and the newSize.height and then adding that difference to the container's height.
First make sure your class adopts the UITextViewDelegate protocol so you can be informed when the text changes like this:
class MyClass: UIViewContoller, UITextViewDelegate
Next define this variable somewhere in your class so that you can keep track of the height in a constraint:
var textHeightConstraint: NSLayoutConstraint!
Next add the following constraint and activate it:
self.textHeightConstraint = addtextview.heightAnchor.constraint(equalToConstant: 40)
self.textHeightConstraint.isActive = true
(if you don't do this in viewDidLoad you need to make textHeightConstraint an optional)
Next subscribe to the delegate (if not already done):
addTextView.delegate = self
Add this function which recalculates the height constraint:
func adjustTextViewHeight() {
let fixedWidth = addtextview.frame.size.width
let newSize = addtextview.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
self.textHeightConstraint.constant = newSize.height
self.view.layoutIfNeeded()
}
Next add a call to that function after the constraints are created to set the initial size:
self.adjustTextViewHeight()
Finally add this method to adjust the height whenever the text changes:
func textViewDidChange(_ textView: UITextView) {
self.adjustTextViewHeight()
}
Just in case that is all confusing here is a minimal example in a sub class of a UIViewController:
class ViewController: UIViewController, UITextViewDelegate {
@IBOutlet var textView: UITextView!
@IBOutlet var textHolder: UIView!
var textHeightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
textView.leadingAnchor.constraint(equalTo: textHolder.leadingAnchor, constant: 8).isActive = true
textView.trailingAnchor.constraint(equalTo: textHolder.trailingAnchor, constant: -8).isActive = true
textView.topAnchor.constraint(equalTo: textHolder.topAnchor, constant: 8).isActive = true
textView.bottomAnchor.constraint(equalTo: textHolder.bottomAnchor, constant: -40).isActive = true
self.textHeightConstraint = textView.heightAnchor.constraint(equalToConstant: 40)
self.textHeightConstraint.isActive = true
self.adjustTextViewHeight()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textViewDidChange(_ textView: UITextView) {
self.adjustTextViewHeight()
}
func adjustTextViewHeight() {
let fixedWidth = textView.frame.size.width
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
self.textHeightConstraint.constant = newSize.height
self.view.layoutIfNeeded()
}
}
There's a clever answer here in objective-c where you can detect a newline character in your textView, and adjust your textView's frame appropriately. Here is the swift version:
var previousPosition:CGRect = CGRect.zero
func textViewDidChange(_ textView: UITextView) {
let position:UITextPosition = textView.endOfDocument
let currentPosition:CGRect = textView.caretRect(for: position)
if(currentPosition.origin.y > previousPosition.origin.y){
/*Update your textView's height here*/
previousPosition = currentPosition
}
}
Make sure that the textView's view controller adopts the UITextViewDelegate
and sets self.textView.delegate = self
Demo Here the textView grows down, but having it grow up is a matter of your constraints.
After implementing the selected answer's solution from "Upholder Of Truth", I was wondering how to get rid of the weird text bottom offset that is brought by the changing of the height constraint.
A simple solution is setting this at the start of your initialisations:
textView.isScrollEnabled = false
Finally, if you need your textview to stop growing after a specific height, you can change the "adjustTextViewHeight" function to this:
func adjustTextViewHeight() {
let fixedWidth = growingTextView.frame.size.width
let newSize = growingTextView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
if newSize.height > 100 {
growingTextView.isScrollEnabled = true
}
else {
growingTextView.isScrollEnabled = false
textViewHeightConstraint.constant = newSize.height
}
view.layoutSubviews()
}
Hope this helps :)