-1

I am using a UITextView inside a tableView cell to hold varying sized text content with scrolling disabled.

In order to auto-size the UITextView I've used auto-layout to pin it to the layout and also added this method to adjust the height:

 override func viewWillAppear(_ animated: Bool) {
    tableView.estimatedRowHeight = 50
    tableView.rowHeight = UITableViewAutomaticDimension
 }

This works correctly on the initial view - when the content first loads. However, I also want the user to be able to edit the text when they tap into the content (similar to the Apple Reminders app). This works correctly with one limitation: UITextView does not expand as the content grows.

How do I enable UITextView to expand during editing without scrolling?

New details: Here is a screenshot of the current settings.

screenshot of textView settings

Per Matt's recommendations below, I created the following subclass.

class MyTextView: UITextView {

        @IBOutlet var heightConstraint : NSLayoutConstraint?
        override func awakeFromNib() {
            super.awakeFromNib()
            self.heightConstraint?.isActive = false
        }
}

I had to modify the forced unwrapping to avoid a fatal error.

forrest
  • 10,570
  • 25
  • 70
  • 132
  • i would recommend using https://github.com/KennethTsang/GrowingTextView framework, it works great – Unreal Developer Aug 15 '18 at 17:39
  • This looks like it would solve the problem nicely, but I would like to do something native. I'll do this if nothing else works. Thanks. – forrest Aug 15 '18 at 17:50

3 Answers3

2

How do I enable UITextView to expand during editing without scrolling

A self-sizing text view is very simple; for a non-scrolling text view with no height constraint, it's the default. In this example, I've added some code to remove the existing height constraint, though you could do that in the storyboard just by indicating that the height constraint is a placeholder:

class MyTextView : UITextView {
    @IBOutlet var heightConstraint : NSLayoutConstraint!
    override func awakeFromNib() {
        super.awakeFromNib()
        self.heightConstraint.isActive = false
    }
}

Screencast of the result:

enter image description here

If you subsequently do a batch update on the table view, and assuming the cell's other internal constraints are right, the cell will be remeasured as well (but I didn't demonstrate that as part of the example).

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Hi Matt, does this work in realtime? When I type does it automatically expand the TextView area? – forrest Aug 15 '18 at 21:54
  • When I add this to the TableView Controller where the UITextView is implemented I get three errors, the first of which is "Method does not override any method from its superclass". The other is "UITableViewController has no member layoutSubviews" and "no member contentSize". Can you provide more insights on the correct implementation? – forrest Aug 15 '18 at 22:34
  • Well, if you added the code to a table view controller, then you didn’t do what I said. Try reading what I said. – matt Aug 15 '18 at 22:35
  • Sorry, my code was wrong, fixed it. It turns out that self-sizing is the default; you don't have to do anything at all except turn off the height constraint. – matt Aug 15 '18 at 22:57
  • please see my notes above. I tried what you said and it threw a fatal error, so I removed the forced unwrapping and made it optional. Your example is exactly what I am looking for so if we can get that last little adjustment, it will be perfect. – forrest Aug 16 '18 at 12:26
  • Sorry but I don't know what you're asking at this point. If there is no height constraint then just delete the code entirely. You don't need a subclass. As I said, self-sizing is the default. – matt Aug 16 '18 at 12:49
  • My goal is to solve the problem. When I came into this post had textViews that would resize according to the preloaded content. However, when I try to edit the content, the textView does not expand dynamically. Your example appears to do exactly what I am looking for. I am asking to bridge the gap between my code and yours. – forrest Aug 16 '18 at 15:13
  • I'm confused. Are you saying you can't make _any_ text view behave as I've demonstrated? If so, would it help you have a downloadable example? – matt Aug 16 '18 at 15:28
  • Yes it would help a lot. – forrest Aug 16 '18 at 15:29
  • https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/bk2ch10p531selfSizingTextView (though as it turns out that is probably actually more sophisticated than you need; my idea in the example was to make the text view self-sizing _and_ to limit that size). – matt Aug 16 '18 at 15:31
  • It took me a bit to review your code and see how I could apply it to my situation. Basically you are disabling scrolling and setting the heightConstraint to false. Is that correct? – forrest Aug 16 '18 at 18:17
  • Yes but, as I just said, you don't need to do that because you have no height constraint (it seems) and scrolling is already disabled. — However, that's not quite the point; the question is, did you see the text view size itself when you type, as in the screencast? If so, that shows what I said, that text views are self-sizing. – matt Aug 16 '18 at 18:35
  • Matt. I am sorry that I was not able to get that solution to work with my code. I tried every possible option. In the end I found something that did work and I have added it as an answer below. Thank you for your efforts. – forrest Aug 16 '18 at 19:55
  • That's great but it is actually what I said in my answer. "Do a batch update on the table view" means to call `beginUpdates` and `endUpdates` (although in actual fact you should be calling `performBatchUpdates` instead). I am pretty sure that you should _not_ have to do the `sizeThatFits` calculation yourself if the table is set up for self-sizing cells and your constraints are right. – matt Aug 16 '18 at 19:59
0

Everyone was very diligent about trying to help me resolve this issue. I tried each one and was not able to implement any of them with satisfactory results.

I was directed to this solution by an associate: https://stackoverflow.com/a/36070002/152205 and with the following modifications was able to solve my problem.

    // MARK: UITextViewDelegate
    func textViewDidChange(_ textView: UITextView) {
        let startHeight = textView.frame.size.height
        let calcHeight = textView.sizeThatFits(textView.frame.size).height

        if startHeight != calcHeight {

            UIView.setAnimationsEnabled(false)
            self.tableView.beginUpdates()
            self.tableView.endUpdates()

          // let scrollTo = self.tableView.contentSize.height - self.tableView.frame.size.height
          // self.tableView.setContentOffset(CGPoint(x: 0, y: scrollTo), animated: false)

            UIView.setAnimationsEnabled(true)
        }
    }

Note: The scrollTo option caused the content to shift up several cell. With that removed everything worked as expected.

forrest
  • 10,570
  • 25
  • 70
  • 132
-1

you could use var sizeThatFits

override func viewDidLoad() {
    super.viewDidLoad()
    textView = UITextView()
    textView.sizeThatFits(CGSize(width: textView.frame.size.width, height: textView.frame.size.height))
}