15

I'm creating a comment section much like the one Facebook uses for it's messaging section in it's iOS app. I want the UITextView to resize the height so the text I'm typing fits inside it rather than you having to scroll to see the text that overflows. Any ideas how I might go about doing this? I've looked into maybe using a CGRect that is assigned to the size and height of the text view which then matches the content size:

CGRect textFrame = textView.frame;
textFrame.size.height = textView.contentSize.height;
textView.frame = textFrame;

I assume I need some sort of function that detects when the text reaches the bounds of the UITextView and then resizes the height of the view? Has anyone struggled with this same concept?

sunkehappy
  • 8,970
  • 5
  • 44
  • 65
Ollie177
  • 315
  • 1
  • 3
  • 17
  • You can find detailed working solution [here](http://stackoverflow.com/a/37633816/2066428) – malex Jun 04 '16 at 18:55

5 Answers5

23

You can adjust frame in this delegate method, do not forget to set textView's delegate to self.

-(BOOL)textView:(UITextView *)_textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
      [self adjustFrames];  
      return YES;
}


-(void) adjustFrames
{
   CGRect textFrame = textView.frame;
   textFrame.size.height = textView.contentSize.height;
   textView.frame = textFrame;
}

this solution is for iOS6 and prior... for iOS7 refer to this

StackOverflow Answer

Community
  • 1
  • 1
waheeda
  • 1,097
  • 1
  • 9
  • 18
8

This is my solution, using autolayout and textView.contentSize.height. Tested on iOS8 Xcode6.3 beta4.

There's one catch about the setContentOffset at the end. I put it to avoid "wrong contentOffset" artefact when line count changes. It adds an extra unwanted blank space below the last line and it doesn't look very nice unless you set it back right after changing the constraint. Took me hours to figure this out!

// set this up somewhere
let minTextViewHeight: CGFloat = 32
let maxTextViewHeight: CGFloat = 64

func textViewDidChange(textView: UITextView) {

    var height = ceil(textView.contentSize.height) // ceil to avoid decimal

    if (height < minTextViewHeight + 5) { // min cap, + 5 to avoid tiny height difference at min height
        height = minTextViewHeight
    }
    if (height > maxTextViewHeight) { // max cap
        height = maxTextViewHeight
    }

    if height != textViewHeight.constant { // set when height changed
        textViewHeight.constant = height // change the value of NSLayoutConstraint
        textView.setContentOffset(CGPointZero, animated: false) // scroll to top to avoid "wrong contentOffset" artefact when line count changes
    }
}
Zac
  • 423
  • 1
  • 6
  • 18
Hlung
  • 13,850
  • 6
  • 71
  • 90
  • where is `textViewHeight` declared? – lf215 Sep 22 '16 at 03:41
  • 1
    @lf215 it's a `NSLayoutConstraint` object for the height of the UITextView. I forgot to add sorry. – Hlung Sep 22 '16 at 06:12
  • I'm pretty new to this. Can you please elaborate/add more code to show how to get textViewHeight? – lf215 Sep 22 '16 at 19:00
  • 1
    @lf215 If you are using storyboard, then it will be an IBOutlet variable like `@IBOutlet weak var textViewHeight: NSLayoutConstraint!` which you connect to the height constraint of your `UITextView` in interface builder. – Hlung Sep 28 '16 at 06:29
  • this works pretty well for me and i'm happy but i wonder if there's something small i could change to make it even better. the cursor seems to always be vertically centered. this looks good with one line: dropbox.com/s/dwjbjhcm80a6co5/Screenshot%202016-09-28%2022.54.10.png but not so good with two lines: https://www.dropbox.com/s/7216694bohuqfin/Screenshot%202016-09-28%2022.57.17.png ideally the second line would stay flat on the bottom. I assume this is because of the cursor vertically centered. – lf215 Sep 29 '16 at 02:58
  • This almost worked for me, but I had to add this line between setting the constraint and the content offset: `textView.bounds.size.height = textView.contentSize.height`. Also I noticed that using `textView.contentOffset = CGPointZero` doesn't work. – Nickkk Jan 12 '17 at 17:35
5

On the TableViewController that holds the UITextView, update the data from the tableViewDataSource that gets put in the cell and then simply call this:

tableView.beginUpdates()
tableView.endUpdates()

Unlike tableView.reloadData(), this does not call resignFirstResponder

Forge
  • 6,538
  • 6
  • 44
  • 64
  • This is perfect! Thanks. Here the same in ObjC: `- (void)textViewDidChange:(UITextView *)textView { [self.tableView beginUpdates]; [self.tableView endUpdates]; }` You even get the animation for free ;) – Holtwick Feb 27 '18 at 10:33
5

First set the minimum height constraints to your TextView:

textView.heightAnchor.constraint(greaterThanOrEqualTo: view.heightAnchor, multiplier: 0.20)

(Make sure you are setting greaterThanOrEqualTo Constraint so that if intrinsic content height is more than this height, it takes intrinsic content height )

OR simple constant

textView.heightAnchor.constraint(greaterThanOrEqualToConstant: someConstant)

While configuring your textView, set isScrollEnabled to false

textView.isScrollEnabled = false

Now when you type on the textView, its intrinsic content size height will increase and it will push views below it automatically.

1

contentsize will not work in ios 7. Try this:

CGFloat textViewContentHeight = textView.contentSize.height;

 textViewContentHeight = ceilf([textView sizeThatFits:textView.frame.size].height + 9);
san
  • 3,350
  • 1
  • 28
  • 40
gunas
  • 1,889
  • 1
  • 16
  • 29