HiveBrain v1.2.0
Get Started
← Back to all entries
patternswiftMinor

Allow UITextView to resize as the user is typing

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
theuitextviewresizeuserallowtyping

Problem

I want to be sure that in terms of auto-layout or just following the proper methods of doing the following is acceptable. I am not sure if there will be an constraint issues or efficiency problems that could slow down my app. It is working so far, I am in the process of trying to animate it as it resizes.

import UIKit

class PickUpViewController: UIViewController, UITextViewDelegate {

    @IBOutlet weak var addressTextVIew: TextViewAutoHeight!
    override func viewDidLoad() {
        super.viewDidLoad()
        addressTextVIew.delegate = self
    }

    func textViewDidChange(_ textView: UITextView) {
        if textView.contentSize.height > textView.frame.size.height {

            let fixedWidth = textView.frame.size.width
            textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))

            var newFrame = textView.frame
            let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))

            newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)

            textView.frame = newFrame;
        }
    }
}

Solution

It can actually be a lot simpler!

UITextView is a UIScrollView where by default the contentSize updates to fit whatever text is in the current view.

Assuming that you have constraints set up (e.g. pinning left, top, and right anchors so that the height is free to grow) then it's as simple as overriding the default value of isScrollEnabled and setting it to false.

You could do that either in your storyboard, or in your text view subclass.

I would suggest moving any logic you have around animating size of the text view out of the view controller and bundle it into your text view subclass.

Assuming that your constraints are set up in the storyboard, your view controller (at this point) doesn't need to have anything:

import UIKit 

class ViewController: UIViewController {

}


Your text view subclass should have everything it needs to do to animate it's height change, and keeping the code there will make your view controllers more lightweight, and your TextViewAutoHeight more reusable in different scenarios.

Your current code doesn't actually animate the height so it's off-topic for /CodeReview, so I suggest searching stack overflow for questions around "UIView animate with duration", and perhaps reposting here when you have working code ready for review.

import UIKit

class TextViewAutoHeight: UITextView {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        isScrollEnabled = false
        NotificationCenter.default.addObserver(self, selector: #selector(updateHeight), name: NSNotification.Name.UITextViewTextDidChange, object: nil)
    }

    func updateHeight() {
        // trigger your animation here

        /*
        var newFrame = frame

        let fixedWidth = frame.size.width
        let newSize = sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))

        newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
        self.frame = newFrame
        */

        // suggest searching stackoverflow for "uiview animatewithduration" for frame-based animation
        // or "animate change in intrinisic size" to learn about a more elgant solution :)
    }
}

Code Snippets

import UIKit 

class ViewController: UIViewController {

}
import UIKit

class TextViewAutoHeight: UITextView {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        isScrollEnabled = false
        NotificationCenter.default.addObserver(self, selector: #selector(updateHeight), name: NSNotification.Name.UITextViewTextDidChange, object: nil)
    }

    func updateHeight() {
        // trigger your animation here

        /*
        var newFrame = frame

        let fixedWidth = frame.size.width
        let newSize = sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))

        newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
        self.frame = newFrame
        */

        // suggest searching stackoverflow for "uiview animatewithduration" for frame-based animation
        // or "animate change in intrinisic size" to learn about a more elgant solution :)
    }
}

Context

StackExchange Code Review Q#151454, answer score: 4

Revisions (0)

No revisions yet.