UITextView height to change dynamically when typing / without using storyboards

Osama Naeem picture Osama Naeem · Jan 8, 2018 · Viewed 9.3k times · Source

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.

enter image description here

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.

Answer

Upholder Of Truth picture Upholder Of Truth · Jan 8, 2018

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