How do I size a UITextView to its content?

406,167

Solution 1

This works for both iOS 6.1 and iOS 7:

- (void)textViewDidChange:(UITextView *)textView
{
    CGFloat fixedWidth = textView.frame.size.width;
    CGSize newSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)];
    CGRect newFrame = textView.frame;
    newFrame.size = CGSizeMake(fmaxf(newSize.width, fixedWidth), newSize.height);
    textView.frame = newFrame;
}

Or in Swift (Works with Swift 4.1 in iOS 11)

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

If you want support for iOS 6.1 then you should also:

textview.scrollEnabled = NO;

Solution 2

This no longer works on iOS 7 or above

There is actually a very easy way to do resizing of the UITextView to its correct height of the content. It can be done using the UITextView contentSize.

CGRect frame = _textView.frame;
frame.size.height = _textView.contentSize.height;
_textView.frame = frame;

One thing to note is that the correct contentSize is only available after the UITextView has been added to the view with addSubview. Prior to that it is equal to frame.size

This will not work if auto layout is ON. With auto layout, the general approach is to use the sizeThatFits method and update the constant value on a height constraint.

CGSize sizeThatShouldFitTheContent = [_textView sizeThatFits:_textView.frame.size];
heightConstraint.constant = sizeThatShouldFitTheContent.height;

heightConstraint is a layout constraint that you typically setup via a IBOutlet by linking the property to the height constraint created in a storyboard.


Just to add to this amazing answer, 2014, if you:

[self.textView sizeToFit];

there is a difference in behaviour with the iPhone6+ only:

enter image description here

With the 6+ only (not the 5s or 6) it does add "one more blank line" to the UITextView. The "RL solution" fixes this perfectly:

CGRect _f = self.mainPostText.frame;
_f.size.height = self.mainPostText.contentSize.height;
self.mainPostText.frame = _f;

It fixes the "extra line" problem on 6+.

Solution 3

Update

The key thing you need to do is turn off scrolling in your UITextView.

myTextView.scrollEnabled = @NO

Original Answer

To make a dynamically sizing UITextView inside a UITableViewCell, I found the following combination works in Xcode 6 with the iOS 8 SDK:

  • Add a UITextView to a UITableViewCell and constrain it to the sides

  • Set the UITextView's scrollEnabled property to NO. With scrolling enabled, the frame of the UITextView is independent of its content size, but with scrolling disabled, there is a relationship between the two.

  • If your table is using the original default row height of 44 then it will automatically calculate row heights, but if you changed the default row height to something else, you may need to manually switch on auto-calculation of row heights in viewDidLoad:

     tableView.estimatedRowHeight = 150;
     tableView.rowHeight = UITableViewAutomaticDimension;
    

For read-only dynamically sizing UITextViews, that’s it. If you’re allowing users to edit the text in your UITextView, you also need to:

  • Implement the textViewDidChange: method of the UITextViewDelegate protocol, and tell the tableView to repaint itself every time the text is edited:

     - (void)textViewDidChange:(UITextView *)textView;
     {
         [tableView beginUpdates];
         [tableView endUpdates];
     }
    
  • And don’t forget to set the UITextView delegate somewhere, either in Storyboard or in tableView:cellForRowAtIndexPath:

Solution 4

Very easy working solution using code and storyboard both.

By Code

textView.scrollEnabled = false

By Storyboard

Uncheck the Scrolling Enable

enter image description here

No need to do anything apart of this.

Solution 5

Swift :

textView.sizeToFit()
Share:
406,167
drewh
Author by

drewh

A shark on whiskey is mighty risky; a shark on beer is a beer engineer!

Updated on August 09, 2021

Comments

  • drewh
    drewh almost 3 years

    Is there a good way to adjust the size of a UITextView to conform to its content? Say for instance I have a UITextView that contains one line of text:

    "Hello world"
    

    I then add another line of text:

    "Goodbye world"
    

    Is there a good way in Cocoa Touch to get the rect that will hold all of the lines in the text view so that I can adjust the parent view accordingly?

    As another example, look at the notes' field for events in the Calendar application - note how the cell (and the UITextView it contains) expands to hold all lines of text in the notes' string.

  • Kevlar
    Kevlar over 14 years
    That gets the size as if it would draw the text on a UILabel. If you set that size to the uitextview's frame then you will still have some scrolling necessary in it.
  • leviathan
    leviathan almost 14 years
    This doesn't work! It causes an error: "lvalue required as left operand of assignment"
  • Lukasz
    Lukasz over 13 years
    You saved my day. I was struggling with selfCalculating class function extension forgetting about this. Thank You.
  • R. Dewi
    R. Dewi over 13 years
    @Ronnie, thank you for your code, i've use your code, but when my textview content reach 2000 line, this code made my app getting slow, can you tell me why??
  • Admin
    Admin about 13 years
    Here the '5' in y - coordinate of UILabel is the gap that I want between textView and Label.
  • Yogesh
    Yogesh about 13 years
    @Ronnie , where should we write this code. I would like to display one line of frame initially and as the user types in the frame should increase to accomodate the text without scrolling up.
  • Hot Licks
    Hot Licks almost 13 years
    Curiouser: When I reduce the text, so that contentSize is only 79 tall, adding 100 I get only 1 line displayed. I have to add 200 -- set UITextView height to 279 -- to get the 3 lines to display.
  • Hot Licks
    Hot Licks almost 13 years
    Aha! I was setting text view height and then adjusting the height of the containing view. Apparently when I did that the text view height was being adjusted. Setting the containing view height first makes things work as expected (or, better, hoped for).
  • Rodrigo
    Rodrigo over 12 years
    Using [_textView sizeToFit] refreshes the contentSize property, removing the requirement to add it to the scrollView beforehand.
  • Maciej Swic
    Maciej Swic over 11 years
    You can also add frame.size.height += (_textView.contentInset.top + _textView.contentInset.bottom); If you have changed the contentInset to avoid the blank frame around the text.
  • jweyrich
    jweyrich about 11 years
    You can only assign to textView.frame.
  • Bo.
    Bo. about 11 years
    _textView.scrollEnabled = NO; is also needed. Otherwise, I ended with a shifted content after a newline.
  • Adam Carter
    Adam Carter about 11 years
    This is so obvious now you said it, I was trying to calculate a height with the sizeWithFont:forWidth:lineBreakMode, thanks!
  • Xander Dunn
    Xander Dunn about 11 years
    This is a great solution, but it doesn't work when using autolayout. Do you have any ideas on accomplishing this with autolayout turned on?
  • phatmann
    phatmann almost 11 years
    If using autolayout, call layoutIfNeeded on the superview. Then you can grab the height from contentSize.
  • Stian Høiland
    Stian Høiland over 10 years
    Where can I read more about this? I'm totally lost as to the seemingly new behaviour in iOS 7 regarding this.
  • Henrik Erlandsson
    Henrik Erlandsson over 10 years
    If you have made it work on iOS 7, please help in this question: stackoverflow.com/questions/19049532/…
  • Henrik Erlandsson
    Henrik Erlandsson over 10 years
    It's about resizing the outer container though. That behavior has changed in iOS 7. I show in the linked question where to add sizeToFit and layoutIfNeeded.
  • Tchelow
    Tchelow over 10 years
    What's the meaning of the variable CGSize size?
  • giorashc
    giorashc over 10 years
    @HenrikErlandsson your comment saved me a lot of work right now :)
  • phatmann
    phatmann over 10 years
    I removed size, it was not supposed to be there.
  • Alex
    Alex over 10 years
    Tested on iOS 7 to resize an UITextView into an UITableViewCell. Works really great. This is the only answer that finally worked for me. Thanks!
  • Brian
    Brian over 10 years
    I needed the textview.scrollEnabled = NO; to prevent the text from getting cut off even in iOS 7.
  • michaelsnowden
    michaelsnowden almost 10 years
    sizetofit saves the day
  • eonil
    eonil over 9 years
    I recommend to use CGFLOAT_MAX macro instead of MAXFLOAT. Proper on both of 32/64 bit platforms.
  • scientiffic
    scientiffic over 9 years
    this worked for me, but then when I try to scroll the parent view to the textView when the user goes to edit it, the origin is off...
  • rob mayoff
    rob mayoff over 9 years
    Use CGFLOAT_MAX, not FLT_MAX.
  • Scooter
    Scooter over 9 years
    I wish I could give you more than one up vote for this awesome solution!!
  • user3344977
    user3344977 over 9 years
    Had to disable autolayout for my nib file for this to work properly, otherwise the first and second characters of a new line distorted the text view's size.
  • fnc12
    fnc12 over 9 years
    Also you can use INFINITY instead of CGFLOAT_MAX and MAXFLOAT. It looks more funny
  • Logic
    Logic about 9 years
    perfect result in iOS 8.1
  • eremzeit
    eremzeit about 9 years
    The question is really language agnostic. Is it really necessary to go back and write swift ports for all answers on UIKit related questions? Seems like that makes for a noisy conversation.
  • Fareed Alnamrouti
    Fareed Alnamrouti about 9 years
    the problem that most of these questions has no specific programming language so you will find it in google even if you search for swift language
  • smmelzer
    smmelzer about 9 years
    Best answer for me. Worked perfectly and UITableViewAutomaticDimension was added in iOS 5 so it is backwards-compatible.
  • Massmaker
    Massmaker almost 9 years
    Sorry, what does it mean: let attribute = [NSFontAttributeName: textView.text.font!] Is this really Swift code without errors?) Did you mean let attribute = [NSFontAttributeName: textView.font!]
  • Antzi
    Antzi almost 9 years
    This kind of answers are discouraged. You should post the relevant code here.
  • ajay_nasa
    ajay_nasa over 8 years
    The one who face little fluctuation before entering in next line add this ` NSRange bottom = NSMakeRange(textView.text.length -1, 1); [textView scrollRangeToVisible:bottom];`
  • Fateh Khalsa
    Fateh Khalsa over 8 years
    Worked great!! NOTE for using Autolayout: I had to set top/bottom space to superview constraints first for this to work.
  • SimonTheDiver
    SimonTheDiver over 8 years
    Didn't quite work for me . The textViewDidChange method cause my tableview to bounce every time i typed a character. What I suggest is that instead of using that method , in the tableView : cellForRowAtIndexPath method, for the row with the textview on it, if you are in a read only mode set .scrollEnabled to NO, and if you are in an editing view set scroll enabled to YES. This way when just displaying it will always show the full text and when editing it will start with the full text and as you add further text will remain the same size with the earlier text scrolling off the top
  • SimonTheDiver
    SimonTheDiver over 8 years
    furthermore I added a >= height constraint to the textview as an empty textfield had no height and couldn't be tapped on to start editing.
  • Christopher Pickslay
    Christopher Pickslay over 8 years
    Excellent solution. For me, the tableView.rowHeight = UITableViewAutomaticDimension was unnecessary.
  • Tomasz Onyszko
    Tomasz Onyszko about 8 years
    In addition, I found that I needed to set scrolling enabled to false for this to work. textView.scrollEnabled = false
  • nekonari
    nekonari about 8 years
    Why are there two separate calls to sizeThatFits(...) in the Swift version? What's the first line for?
  • zztop
    zztop almost 8 years
    This does not work for me in 2016. Possibly out of date. The height of the content is always the frame height even when I added the addSubView
  • Daniel Kuta
    Daniel Kuta almost 8 years
    You should remember to call textView.layoutIfNeeded() after textView.sizeToFit()
  • Daniel Kuta
    Daniel Kuta almost 8 years
    If someone have a problem with resizing, put this code above to override func layoutSubviews() method.
  • pnavk
    pnavk almost 8 years
    This answer is correct. In StoryBoard you have the TextView AutoSize just like a UILabel. All we need to do is set scrollEnabled=false.
  • Francisco Romero
    Francisco Romero over 7 years
    @tmarkiewicz It works when the text fits on the screen of your device but it will be a problem when your text is bigger than the space that there is on the screen. You will not be able to scroll it to see the rest of the text.
  • jbll
    jbll over 7 years
    This works great. Was confused for a few minutes because I still had a height autolayout constraint set in IB for the text view, but after removing it everything is good.
  • Prashant Ghimire
    Prashant Ghimire over 7 years
    there is bug in this method in ios 9.the size of textview get to normal height when selecting text from text view
  • Eric Conner
    Eric Conner over 7 years
    Thank you again Brett Donald! I forgot how to do this and found your answer once again over 1 year later 😀.
  • thibaut noah
    thibaut noah over 7 years
    Padding was the key for me
  • Brett Donald
    Brett Donald over 7 years
    For anyone who comes across this answer in future, the main thing you need to know is simply UITextView.scrollEnabled = @NO
  • Derek
    Derek over 7 years
    Just remember to add this into viewDidLayoutSubviews rather than viewWillAppear
  • Fattie
    Fattie about 7 years
    that's a FANTASTIC nickname!
  • Will Larche
    Will Larche about 7 years
    Setting the constant needs to be followed by [textView layoutIfNeeded]; in the animation block. (Autolayout animations do it like that.)
  • Dan Rosenstark
    Dan Rosenstark about 7 years
    Essentially, this is correct: if you are using Autolayout constraints and shut off scrolling, the whole thing works.
  • Mansuu....
    Mansuu.... over 6 years
    delegate method is not getting called @Peter Stajger
  • Wayne
    Wayne over 6 years
    When textview.scrollEnabled = NO; My cursor is not visible, after typing first character it becomes visible. When scrolling is enabled, when text is entered, the top text is scrolled up too much (over scrolling), after the first character is entered the scrolling fixes itself. So leaving scrolling enabled and using this line fixes it for me: NSRange bottom = NSMakeRange(textView.text.length -1, 1); [textView scrollRangeToVisible:bottom]; As aiay_nasa has posted.
  • Stefanija
    Stefanija about 6 years
    It causes the screen to bounce every time on update as @SimonTheDiver said
  • samir105
    samir105 about 6 years
    Thanks a lot. It was that simple, just disable scrolling in storyboard or code, it will be like UILabel with lines 0.
  • velval
    velval almost 6 years
    This works great, just remember to to make sure you do not have height constraints set for the TextView in interface builder
  • Jake T.
    Jake T. almost 6 years
    @Mansuu.... This isn't a guaranteed solution, as there are things that you can do to override the intrinsic content size, and if you change the text after the view is laid out it won't update unless you set that up, too. But, those things remain true for setting numberOfLines = 0 on a textfield, too. If this doesn't work, you probably have other constraints that will implicitly set a height.
  • iOS Flow
    iOS Flow almost 6 years
    Note: So this answer is just to highlight that you don't have to use the CGFloat.greatestFiniteMagnitude if you're constraint to a certain width or height -> spaceAvailable in my case can be max the screen width - Spacings and other labels width or min the screen width it all depends on what I display...
  • Naresh
    Naresh over 5 years
    Your answer is acceptable, but if we add height constraint it's won't work.
  • Alok
    Alok over 5 years
    @iOS if you are adding height constraint then make constraints priority lower otherwise it will not work.
  • Ben Smith
    Ben Smith over 5 years
    This is the answer!!! No need for more complicated solutions!!! @FranciscoRomero Why not put the textview in a table cell or colleciton view cell then you can always scroll the table or colleciton view....
  • ArielSD
    ArielSD about 5 years
    The sizing is looking great in the UI debugger, but the text isn't wrapping - I put this in initWithFrame, could that be the problem?
  • JLundell
    JLundell almost 5 years
    Alternatively set the textView's height constraint to the calculated height.
  • JLundell
    JLundell almost 5 years
    Re @DanielKuta's suggestion, note that layoutSubviews() is a UIView function, great if you're subclassing the textview or a parent. If you're doing this in a view controller, viewWillLayoutSubviews() is what you want.
  • PakitoV
    PakitoV almost 4 years
    for future reference. If you are using Autolayout go check Alok answer, that's all you should need, as simple as that.
  • pete
    pete almost 4 years
    I don't think this can override constraints. How to specify this initially in the storyboard constraint? I don't even have dynamic text; just static.
  • fumoboy007
    fumoboy007 over 3 years
    Explanation of why this works: stackoverflow.com/a/65861960/815742
  • Vyachaslav Gerchicov
    Vyachaslav Gerchicov almost 3 years
    what if your text view has exclusion paths and you can't just calculate size for rect? In other words what to do if text view shape is not rect?