539

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.

Alok
  • 20,758
  • 4
  • 35
  • 63
drewh
  • 9,795
  • 7
  • 30
  • 41
  • please consider updating correct answer, since the accepted one leads to unnecessary calculations, now there is a contentSize property for this issue. – Juan Boero Dec 18 '15 at 19:43
  • why are there no questions about how to do this non-dynamically? I can't even find out how to do it staticly. And the provided solutions don't seem to override "constraints" in storyboard. – pete Aug 14 '20 at 03:27

41 Answers41

626

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;
APerson
  • 7,304
  • 5
  • 32
  • 48
jhibberd
  • 7,278
  • 1
  • 14
  • 9
  • 1
    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! – Alex Nov 30 '13 at 18:40
  • 41
    I needed the `textview.scrollEnabled = NO;` to prevent the text from getting cut off even in iOS 7. – Brian Jan 06 '14 at 22:06
  • 19
    I recommend to use `CGFLOAT_MAX` macro instead of `MAXFLOAT`. Proper on both of 32/64 bit platforms. – eonil Aug 27 '14 at 14:18
  • 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... – scientiffic Sep 07 '14 at 16:42
  • 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. – user3344977 Nov 29 '14 at 21:16
  • 1
    Also you can use INFINITY instead of CGFLOAT_MAX and MAXFLOAT. It looks more funny – fnc12 Jan 27 '15 at 08:55
  • 3
    The one who face little fluctuation before entering in next line add this ` NSRange bottom = NSMakeRange(textView.text.length -1, 1); [textView scrollRangeToVisible:bottom];` – ajay_nasa Aug 28 '15 at 10:27
  • 1
    Worked great!! NOTE for using Autolayout: I had to set top/bottom space to superview constraints first for this to work. – Fateh Khalsa Sep 14 '15 at 18:57
  • 4
    Why are there two separate calls to sizeThatFits(...) in the Swift version? What's the first line for? – nekonari May 05 '16 at 15:05
  • 1
    If someone have a problem with resizing, put this code above to override func layoutSubviews() method. – Daniel Kuta Aug 12 '16 at 12:04
  • 1
    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. – jbll Oct 06 '16 at 00:40
  • there is bug in this method in ios 9.the size of textview get to normal height when selecting text from text view – Prashant Ghimire Oct 26 '16 at 07:26
  • Just remember to add this into viewDidLayoutSubviews rather than viewWillAppear – Derek Jan 23 '17 at 12:34
  • 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. – Wayne Sep 04 '17 at 07:20
  • This works great, just remember to to make sure you do not have height constraints set for the TextView in interface builder – velval May 29 '18 at 22:48
  • 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? – ArielSD May 02 '19 at 22:24
  • Alternatively set the textView's height constraint to the calculated height. – JLundell Aug 17 '19 at 17:47
  • 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. – JLundell Aug 17 '19 at 17:55
  • for future reference. If you are using Autolayout go check Alok answer, that's all you should need, as simple as that. – PakitoV Jun 11 '20 at 13:52
  • 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. – pete Aug 14 '20 at 03:45
577

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+.

Jakub
  • 12,851
  • 13
  • 75
  • 130
Ronnie Liew
  • 18,000
  • 14
  • 43
  • 50
  • 2
    You saved my day. I was struggling with selfCalculating class function extension forgetting about this. Thank You. – Lukasz Jan 05 '11 at 10:52
  • @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?? – R. Dewi Jan 28 '11 at 07:52
  • @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. – Yogesh Apr 28 '11 at 04:03
  • 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 Jun 23 '11 at 17:42
  • 4
    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). – Hot Licks Jun 23 '11 at 17:52
  • 11
    Using [_textView sizeToFit] refreshes the contentSize property, removing the requirement to add it to the scrollView beforehand. – Rodrigo Nov 22 '11 at 02:15
  • 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. – Maciej Swic Dec 27 '12 at 15:53
  • _textView.scrollEnabled = NO; is also needed. Otherwise, I ended with a shifted content after a newline. – Bo. Mar 15 '13 at 11:07
  • This is so obvious now you said it, I was trying to calculate a height with the `sizeWithFont:forWidth:lineBreakMode`, thanks! – Adam Carter Mar 26 '13 at 19:47
  • 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? – Xander Dunn Apr 15 '13 at 04:29
  • 1
    If using autolayout, call `layoutIfNeeded` on the superview. Then you can grab the height from `contentSize`. – phatmann May 21 '13 at 04:46
  • If you have made it work on iOS 7, please help in this question: http://stackoverflow.com/questions/19049532/how-do-i-size-a-uitextview-to-its-content-on-ios-7 – Henrik Erlandsson Sep 27 '13 at 11:25
  • 4
    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. – Henrik Erlandsson Oct 21 '13 at 06:25
  • @HenrikErlandsson your comment saved me a lot of work right now :) – giorashc Nov 17 '13 at 19:44
  • 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 – zztop May 20 '16 at 20:46
96

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:

Amal T S
  • 3,037
  • 2
  • 22
  • 51
Brett Donald
  • 2,888
  • 3
  • 18
  • 38
  • Best answer for me. Worked perfectly and UITableViewAutomaticDimension was added in iOS 5 so it is backwards-compatible. – smmelzer Apr 09 '15 at 14:23
  • 4
    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 Dec 18 '15 at 10:57
  • 3
    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. – SimonTheDiver Dec 18 '15 at 11:04
  • Excellent solution. For me, the `tableView.rowHeight = UITableViewAutomaticDimension` was unnecessary. – Christopher Pickslay Jan 21 '16 at 02:13
  • Thank you again Brett Donald! I forgot how to do this and found your answer once again over 1 year later . – Eric Conner Dec 01 '16 at 01:26
  • For anyone who comes across this answer in future, the main thing you need to know is simply UITextView.scrollEnabled = @NO – Brett Donald Jan 05 '17 at 00:51
  • It causes the screen to bounce every time on update as @SimonTheDiver said – Stefanija Feb 22 '18 at 13:50
91

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.

Alok
  • 20,758
  • 4
  • 35
  • 63
  • 5
    This answer is correct. In StoryBoard you have the TextView AutoSize just like a UILabel. All we need to do is set scrollEnabled=false. – pnavk Aug 12 '16 at 21:01
  • 4
    Essentially, this is correct: if you are using Autolayout constraints and shut off scrolling, the whole thing works. – Dan Rosenstark Apr 10 '17 at 14:38
  • 3
    Thanks a lot. It was that simple, just disable scrolling in storyboard or code, it will be like UILabel with lines 0. – samir105 Mar 10 '18 at 07:24
  • @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. – Jake T. Jul 05 '18 at 17:15
  • Your answer is acceptable, but if we add height constraint it's won't work. – iOS Aug 28 '18 at 06:31
  • 3
    @iOS if you are adding height constraint then make constraints priority lower otherwise it will not work. – Alok Aug 28 '18 at 07:21
  • Explanation of why this works: https://stackoverflow.com/a/65861960/815742 – fumoboy007 Jan 23 '21 at 17:07
63

Swift :

textView.sizeToFit()
Juan Boero
  • 5,451
  • 37
  • 56
  • 14
    In addition, I found that I needed to set scrolling enabled to false for this to work. `textView.scrollEnabled = false` – tmarkiewicz Mar 19 '16 at 18:52
  • 1
    You should remember to call textView.layoutIfNeeded() after textView.sizeToFit() – Daniel Kuta Jun 01 '16 at 11:24
  • 5
    @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. – Francisco Romero Oct 03 '16 at 14:48
  • 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.... – Ben Smith Feb 05 '19 at 11:36
24

In iOS6, you can check the contentSize property of UITextView right after you set the text. In iOS7, this will no longer work. If you want to restore this behavior for iOS7, place the following code in a subclass of UITextView.

- (void)setText:(NSString *)text
{
    [super setText:text];

    if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1) {
        CGRect rect = [self.textContainer.layoutManager usedRectForTextContainer:self.textContainer];
        UIEdgeInsets inset = self.textContainerInset;
        self.contentSize = UIEdgeInsetsInsetRect(rect, inset).size;
    }
}
Stian Høiland
  • 3,545
  • 2
  • 25
  • 32
phatmann
  • 17,007
  • 6
  • 57
  • 47
24

In my (limited) experience,

- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode

does not respect newline characters, so you can end up with a lot shorter CGSize than is actually required.

- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size

does seem to respect the newlines.

Also, the text isn't actually rendered at the top of the UITextView. In my code, I set the new height of the UITextView to be 24 pixels larger than the height returned by the sizeOfFont methods.

Venk
  • 5,861
  • 8
  • 35
  • 49
user63934
  • 251
  • 2
  • 3
  • 3
    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. – Kevlar Dec 04 '09 at 19:01
19

I will post right solution at the bottom of the page in case someone is brave (or despaired enough) to read to this point.

Here is gitHub repo for those, who don't want to read all that text: resizableTextView

This works with iOs7 (and I do believe it will work with iOs8) and with autolayout. You don't need magic numbers, disable layout and stuff like that. Short and elegant solution.

I think, that all constraint-related code should go to updateConstraints method. So, let's make our own ResizableTextView.

The first problem we meet here is that don't know real content size before viewDidLoad method. We can take long and buggy road and calculate it based on font size, line breaks, etc. But we need robust solution, so we'll do:

CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];

So now we know real contentSize no matter where we are: before or after viewDidLoad. Now add height constraint on textView (via storyboard or code, no matter how). We'll adjust that value with our contentSize.height:

[self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
    if (constraint.firstAttribute == NSLayoutAttributeHeight) {
        constraint.constant = contentSize.height;
        *stop = YES;
    }
}];

The last thing to do is to tell superclass to updateConstraints.

[super updateConstraints];

Now our class looks like:

ResizableTextView.m

- (void) updateConstraints {
    CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];

    [self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
        if (constraint.firstAttribute == NSLayoutAttributeHeight) {
            constraint.constant = contentSize.height;
            *stop = YES;
        }
    }];

    [super updateConstraints];
}

Pretty and clean, right? And you don't have to deal with that code in your controllers!

But wait! Y NO ANIMATION!

You can easily animate changes to make textView stretch smoothly. Here is an example:

    [self.view layoutIfNeeded];
    // do your own text change here.
    self.infoTextView.text = [NSString stringWithFormat:@"%@, %@", self.infoTextView.text, self.infoTextView.text];
    [self.infoTextView setNeedsUpdateConstraints];
    [self.infoTextView updateConstraintsIfNeeded];
    [UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
        [self.view layoutIfNeeded];
    } completion:nil];
Nikita Took
  • 3,920
  • 22
  • 32
14

Did you try [textView sizeThatFits:textView.bounds] ?

Edit: sizeThatFits returns the size but does not actually resize the component. I'm not sure if that's what you want, or if [textView sizeToFit] is more what you were looking for. In either case, I do not know if it will perfectly fit the content like you want, but it's the first thing to try.

Mike McMaster
  • 7,463
  • 8
  • 34
  • 42
10

Another method is the find the size a particular string will take up using the NSString method:

-(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size

This returns the size of the rectangle that fits the given string with the given font. Pass in a size with the desired width and a maximum height, and then you can look at the height returned to fit the text. There is a version that lets you specify line break mode also.

You can then use the returned size to change the size of your view to fit.

Venk
  • 5,861
  • 8
  • 35
  • 49
Code Addict
  • 156
  • 2
9

We can do it by constraints .

  1. Set Height constraints for UITextView. enter image description here

2.Create IBOutlet for that height constraint.

 @property (weak, nonatomic) IBOutlet NSLayoutConstraint *txtheightconstraints;

3.don't forget to set delegate for your textview.

4.

-(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);
    NSLog(@"this is updating height%@",NSStringFromCGSize(newFrame.size));
    [UIView animateWithDuration:0.2 animations:^{
                  _txtheightconstraints.constant=newFrame.size.height;
    }];

}

then update your constraint like this :)

Kishore Kumar
  • 3,639
  • 2
  • 20
  • 45
8

If you don't have the UITextView handy (for example, you're sizing table view cells), you'll have to calculate the size by measuring the string, then accounting for the 8 pt of padding on each side of a UITextView. For example, if you know the desired width of your text view and want to figure out the corresponding height:

NSString * string = ...;
CGFloat textViewWidth = ...;
UIFont * font = ...;

CGSize size = CGSizeMake(textViewWidth - 8 - 8, 100000);
size.height = [string sizeWithFont:font constrainedToSize:size].height + 8 + 8;

Here, each 8 is accounting for one of the four padded edges, and 100000 just serves as a very large maximum size.

In practice, you may want to add an extra font.leading to the height; this adds a blank line below your text, which may look better if there are visually heavy controls directly beneath the text view.

Venk
  • 5,861
  • 8
  • 35
  • 49
Brent Royal-Gordon
  • 16,531
  • 6
  • 53
  • 86
8

Starting with iOS 8, it is possible to use the auto layout features of a UITableView to automatically resize a UITextView with no custom code at all. I have put a project in github that demonstrates this in action, but here is the key:

  1. The UITextView must have scrolling disabled, which you can do programmatically or through the interface builder. It will not resize if scrolling is enabled because scrolling lets you view the larger content.
  2. In viewDidLoad for the UITableViewController, you must set a value for estimatedRowHeight and then set the rowHeight to UITableViewAutomaticDimension.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.estimatedRowHeight = self.tableView.rowHeight;
    self.tableView.rowHeight = UITableViewAutomaticDimension;
}
  1. The project deployment target must be iOS 8 or greater.
Roger
  • 753
  • 9
  • 14
Chuck Krutsinger
  • 2,762
  • 2
  • 23
  • 45
7

Combined with Mike McMaster's answer, you might want to do something like:

[myTextView setDelegate: self];

...

- (void)textViewDidChange:(UITextView *)textView {
  if (myTextView == textView) {
     // it changed.  Do resizing here.
  }
}
Olie
  • 23,933
  • 18
  • 92
  • 129
7

Guys using autolayout and your sizetofit isn't working, then please check your width constraint once. If you had missed the width constraint then the height will be accurate.

No need to use any other API. just one line would fix all the issue.

[_textView sizeToFit];

Here, I was only concerned with height, keeping the width fixed and had missed the width constraint of my TextView in storyboard.

And this was to show up the dynamic content from the services.

Hope this might help..

Swaroop S
  • 481
  • 5
  • 10
7

The following things are enough:

  1. Just remember to set scrolling enabled to NO for your UITextView:

enter image description here

  1. Properly set Auto Layout Constraints.

You may even use UITableViewAutomaticDimension.

Bartłomiej Semańczyk
  • 52,820
  • 43
  • 206
  • 318
7

I found out a way to resize the height of a text field according to the text inside it and also arrange a label below it based on the height of the text field! Here is the code.

UITextView *_textView = [[UITextView alloc] initWithFrame:CGRectMake(10, 10, 300, 10)];
NSString *str = @"This is a test text view to check the auto increment of height of a text view. This is only a test. The real data is something different.";
_textView.text = str;

[self.view addSubview:_textView];
CGRect frame = _textView.frame;
frame.size.height = _textView.contentSize.height;
_textView.frame = frame;

UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(10, 5 + frame.origin.y + frame.size.height, 300, 20)];
lbl.text = @"Hello!";
[self.view addSubview:lbl];
dheeraj
  • 81
  • 1
  • 1
6

I reviewed all the answers and all are keeping fixed width and adjust only height. If you wish to adjust also width you can very easily use this method:

so when configuring your text view, set scroll disabled

textView.isScrollEnabled = false

and then in delegate method func textViewDidChange(_ textView: UITextView) add this code:

func textViewDidChange(_ textView: UITextView) {
    let newSize = textView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
    textView.frame = CGRect(origin: textView.frame.origin, size: newSize)
}

Outputs:

enter image description here

enter image description here

Peter Stajger
  • 1,565
  • 15
  • 25
5

disable scrolling

add constaints

and add your text

[yourTextView setText:@"your text"];
[yourTextView layoutIfNeeded];

if you use UIScrollView you should add this too;

[yourScrollView layoutIfNeeded];

-(void)viewDidAppear:(BOOL)animated{
    CGRect contentRect = CGRectZero;

    for (UIView *view in self.yourScrollView.subviews) {
         contentRect = CGRectUnion(contentRect, view.frame);
    }
    self.yourScrollView.contentSize = contentRect.size;
}
alicanozkara
  • 3,945
  • 1
  • 21
  • 22
5

Using UITextViewDelegate is the easiest way:

func textViewDidChange(_ textView: UITextView) {
    textView.sizeToFit()
    textviewHeight.constant = textView.contentSize.height
}
f0go
  • 192
  • 1
  • 6
4

This worked nicely when I needed to make text in a UITextView fit a specific area:

// The text must already be added to the subview, or contentviewsize will be wrong.

- (void) reduceFontToFit: (UITextView *)tv {
    UIFont *font = tv.font;
    double pointSize = font.pointSize;

    while (tv.contentSize.height > tv.frame.size.height && pointSize > 7.0) {
        pointSize -= 1.0;
        UIFont *newFont = [UIFont fontWithName:font.fontName size:pointSize];
        tv.font = newFont;
    }
    if (pointSize != font.pointSize)
        NSLog(@"font down to %.1f from %.1f", pointSize, tv.font.pointSize);
}
Venk
  • 5,861
  • 8
  • 35
  • 49
Bill Cheswick
  • 615
  • 7
  • 12
4

here is the swift version of @jhibberd

    let cell:MsgTableViewCell! = self.tableView.dequeueReusableCellWithIdentifier("MsgTableViewCell", forIndexPath: indexPath) as? MsgTableViewCell
    cell.msgText.text = self.items[indexPath.row]
    var fixedWidth:CGFloat = cell.msgText.frame.size.width
    var size:CGSize = CGSize(width: fixedWidth,height: CGFloat.max)
    var newSize:CGSize = cell.msgText.sizeThatFits(size)
    var newFrame:CGRect = cell.msgText.frame;
    newFrame.size = CGSizeMake(CGFloat(fmaxf(Float(newSize.width), Float(fixedWidth))), newSize.height);
    cell.msgText.frame = newFrame
    cell.msgText.frame.size = newSize        
    return cell
Fareed Alnamrouti
  • 26,439
  • 4
  • 77
  • 71
  • 6
    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. – eremzeit Apr 04 '15 at 23:55
  • 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 – Fareed Alnamrouti Apr 05 '15 at 00:14
3

For iOS 7.0, instead of setting the frame.size.height to the contentSize.height (which currently does nothing) use [textView sizeToFit].

See this question.

Community
  • 1
  • 1
Stian Høiland
  • 3,545
  • 2
  • 25
  • 32
3

This works fine for Swift 5 in case you want to fit your TextView once user write text on the fly.

Just implement UITextViewDelegate with:

func textViewDidChange(_ textView: UITextView) {
    let newSize = textView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
    textView.frame.size = CGSize(width: newSize.width, height: newSize.height)
}
atereshkov
  • 2,967
  • 1
  • 25
  • 36
2

if any other get here, this solution work for me, 1"Ronnie Liew"+4"user63934" (My text arrive from web service): note the 1000 (nothing can be so big "in my case")

UIFont *fontNormal = [UIFont fontWithName:FONTNAME size:FONTSIZE];

NSString *dealDescription = [client objectForKey:@"description"];

//4
CGSize textSize = [dealDescription sizeWithFont:fontNormal constrainedToSize:CGSizeMake(containerUIView.frame.size.width, 1000)];

CGRect dealDescRect = CGRectMake(10, 300, containerUIView.frame.size.width, textSize.height);

UITextView *dealDesc = [[[UITextView alloc] initWithFrame:dealDescRect] autorelease];

dealDesc.text = dealDescription;
//add the subview to the container
[containerUIView addSubview:dealDesc];

//1) after adding the view
CGRect frame = dealDesc.frame;
frame.size.height = dealDesc.contentSize.height;
dealDesc.frame = frame;

And that is... Cheers

Alejo JM
  • 771
  • 5
  • 12
1

The Best way which I found out to re-size the height of the UITextView according to the size of the text.

CGSize textViewSize = [YOURTEXTVIEW.text sizeWithFont:[UIFont fontWithName:@"SAMPLE_FONT" size:14.0]
                       constrainedToSize:CGSizeMake(YOURTEXTVIEW.frame.size.width, FLT_MAX)];

or You can USE

CGSize textViewSize = [YOURTEXTVIEW.text sizeWithFont:[UIFont fontWithName:@"SAMPLE_FONT" size:14.0]
                       constrainedToSize:CGSizeMake(YOURTEXTVIEW.frame.size.width, FLT_MAX) lineBreakMode:NSLineBreakByTruncatingTail];
Manju
  • 4,083
  • 2
  • 16
  • 12
1

Hope this helps:

- (void)textViewDidChange:(UITextView *)textView {
  CGSize textSize = textview.contentSize;
  if (textSize != textView.frame.size)
      textView.frame.size = textSize;
}
Dr. Marty
  • 27
  • 1
1

For those who want the textview to actually move up and maintain the bottom line position

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

if(frame.size.height > textView.frame.size.height){
    CGFloat diff = frame.size.height - textView.frame.size.height;
    textView.frame = CGRectMake(0, textView.frame.origin.y - diff, textView.frame.size.width, frame.size.height);
}
else if(frame.size.height < textView.frame.size.height){
    CGFloat diff = textView.frame.size.height - frame.size.height;
    textView.frame = CGRectMake(0, textView.frame.origin.y + diff, textView.frame.size.width, frame.size.height);
}
Gal Blank
  • 1,987
  • 2
  • 15
  • 18
1

The only code that will work is the one that uses 'SizeToFit' as in jhibberd answer above but actually it won't pick up unless you call it in ViewDidAppear or wire it to UITextView text changed event.

1

Based on Nikita Took's answer I came to the following solution in Swift which works on iOS 8 with autolayout:

    descriptionTxt.scrollEnabled = false
    descriptionTxt.text = yourText

    var contentSize = descriptionTxt.sizeThatFits(CGSizeMake(descriptionTxt.frame.size.width, CGFloat.max))
    for c in descriptionTxt.constraints() {
        if c.isKindOfClass(NSLayoutConstraint) {
            var constraint = c as! NSLayoutConstraint
            if constraint.firstAttribute == NSLayoutAttribute.Height {
                constraint.constant = contentSize.height
                break
            }
        }
    }
Apfelsaft
  • 5,518
  • 4
  • 25
  • 37
1

Swift answer: The following code computes the height of your textView.

                let maximumLabelSize = CGSize(width: Double(textView.frame.size.width-100.0), height: DBL_MAX)
                let options = NSStringDrawingOptions.TruncatesLastVisibleLine | NSStringDrawingOptions.UsesLineFragmentOrigin
                let attribute = [NSFontAttributeName: textView.font!]
                let str = NSString(string: message)
                let labelBounds = str.boundingRectWithSize(maximumLabelSize,
                    options: NSStringDrawingOptions.UsesLineFragmentOrigin,
                    attributes: attribute,
                    context: nil)
                let myTextHeight = CGFloat(ceilf(Float(labelBounds.height)))

Now you can set the height of your textView to myTextHeight

RawMean
  • 7,547
  • 3
  • 52
  • 79
  • 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!]` – Massmaker Jun 17 '15 at 09:14
1

Here is the answer if you need resize textView and tableViewCell dynamically in staticTableView

[https://stackoverflow.com/a/43137182/5360675][1]

Community
  • 1
  • 1
1

Works like a charm on ios 11 and I'm working in a cell, like a chat cell with bubble.

let content = UITextView(frame: CGRect(x: 4, y: 4, width: 0, height: 0))
content.text = "what ever short or long text you wanna try"
content.textAlignment = NSTextAlignment.left
content.font = UIFont.systemFont(ofSize: 13)
let spaceAvailable = 200 //My cell is fancy I have to calculate it...
let newSize = content.sizeThatFits(CGSize(width: CGFloat(spaceAvailable), height: CGFloat.greatestFiniteMagnitude))
content.isEditable = false
content.dataDetectorTypes = UIDataDetectorTypes.all
content.isScrollEnabled = false
content.backgroundColor = UIColor.clear
bkgView.addSubview(content)
iOS Flow
  • 69
  • 1
  • 8
  • 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... – iOS Flow Jul 12 '18 at 01:25
0

this method seems to work for ios7

 // Code from apple developer forum - @Steve Krulewitz, @Mark Marszal, @Eric Silverberg
- (CGFloat)measureHeight
{
    if ([self respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)])
    {
    CGRect frame = internalTextView.bounds;
    CGSize fudgeFactor;
    // The padding added around the text on iOS6 and iOS7 is different.
    fudgeFactor = CGSizeMake(10.0, 16.0);

    frame.size.height -= fudgeFactor.height;
    frame.size.width -= fudgeFactor.width;

    NSMutableAttributedString* textToMeasure;
    if(internalTextView.attributedText && internalTextView.attributedText.length > 0){
        textToMeasure = [[NSMutableAttributedString alloc] initWithAttributedString:internalTextView.attributedText];
    }
    else{
        textToMeasure = [[NSMutableAttributedString alloc] initWithString:internalTextView.text];
        [textToMeasure addAttribute:NSFontAttributeName value:internalTextView.font range:NSMakeRange(0, textToMeasure.length)];
    }

    if ([textToMeasure.string hasSuffix:@"\n"])
    {
        [textToMeasure appendAttributedString:[[NSAttributedString alloc] initWithString:@"-" attributes:@{NSFontAttributeName: internalTextView.font}]];
    }

    // NSAttributedString class method: boundingRectWithSize:options:context is
    // available only on ios7.0 sdk.
    CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)
                                              options:NSStringDrawingUsesLineFragmentOrigin
                                              context:nil];

    return CGRectGetHeight(size) + fudgeFactor.height;
}
else
{
    return self.internalTextView.contentSize.height;
}
}
madmik3
  • 6,861
  • 3
  • 36
  • 58
0

The easiest way to ask a UITextView is just calling -sizeToFitit should work also with scrollingEnabled = YES, after that check for the height and add a height constraint on the text view with the same value.
Pay attention that UITexView contains insets, this means that you can't ask the string object how much space it want to use, because this is just the bounding rect of the text.
All the person that are experiencing wrong size using -sizeToFit it's probably due to the fact that the text view has not been layout yet to the interface size.
This always happen when you use size classes and a UITableView, the first time cells are created in the - tableView:cellForRowAtIndexPath: the comes out with the size of the any-any configuration, if you compute you value just now the text view will have a different width than the expected and this will screw all sizes.
To overcome this issue I've found useful to override the -layoutSubviews method of the cell to recalculate textview height.

Andrea
  • 24,774
  • 10
  • 79
  • 121
0

If you are using scrollview and content view,and you want to increase the height depending the TextView content height,then this piece of code will help you.

Hope this will help,it worked perfectly in iOS9.2

and of course set textview.scrollEnabled = NO;

-(void)adjustHeightOfTextView
{
    //this pieces of code will helps to adjust the height of the uitextview View W.R.T content
    //This block of code work will calculate the height of the textview text content height and calculate the height for the whole content of the view to be displayed.Height --> 400 is fixed,it will change if you change anything in the storybord.


CGSize textViewSize = [self.textview sizeThatFits:CGSizeMake(self.view.frame.size.width, self.view.frame.size.height)];//calulcate the content width and height

float textviewContentheight =textViewSize.height;
self.scrollview.contentSize = CGSizeMake(self.textview.frame.size.width,textviewContentheight + 400);//height value is passed
self.scrollview.frame =CGRectMake(self.scrollview.frame.origin.x, self.scrollview.frame.origin.y, self.scrollview.frame.size.width, textviewContentheight+400);

CGRect Frame = self.contentview.frame;
Frame.size.height = textviewContentheight + 400;
[self.contentview setFrame:Frame];

self.textview.frame =CGRectMake(self.textview.frame.origin.x, self.textview.frame.origin.y, self.textview.frame.size.width, textviewContentheight);
[ self.textview setContentSize:CGSizeMake( self.textview.frame.size.width,textviewContentheight)];
self.contenview_heightConstraint.constant = 

self.scrollview.bounds.size.height;
    NSLog(@"%f",self.contenview_heightConstraint.constant);
}
jithin
  • 429
  • 7
  • 20
0

It's quite easy with Key Value Observing (KVO), just create a subclass of UITextView and do:

private func setup() { // Called from init or somewhere

    fitToContentObservations = [
        textView.observe(\.contentSize) { _, _ in
            self.invalidateIntrinsicContentSize()
        },
        // For some reason the content offset sometimes is non zero even though the frame is the same size as the content size.
        textView.observe(\.contentOffset) { _, _ in
            if self.contentOffset != .zero { // Need to check this to stop infinite loop
                self.contentOffset = .zero
            }
        }
    ]
}
public override var intrinsicContentSize: CGSize {
    return contentSize
}

If you don't want to subclass you could try doing textView.bounds = textView.contentSize in the contentSize observer.

Jonathan.
  • 51,850
  • 46
  • 174
  • 275
0

The simplest solution that worked for me was to put a height constraint on the textView in the Storyboard, then connect the textView and height constraint to the code:

@IBOutlet var myAwesomeTextView: UITextView!
@IBOutlet var myAwesomeTextViewHeight: NSLayoutConstraint!

Then after setting the text and paragraph styles, add this in viewDidAppear:

self.myAwesomeTextViewHeight.constant = self.myAwesomeTextView.contentSize.height

Some notes:

  1. In contrast to other solutions, isScrollEnabled must to be set to true in order for this to work.
  2. In my case, I was setting custom attributes to the font inside the code, therefore I had to set the height in viewDidAppear (it wouldn't work properly before that). If you aren't changing any text attributes in code, you should be able to set the height in viewDidLoad or anywhere after setting the text.
iOS_Mouse
  • 335
  • 2
  • 9
0

Looks like this guy figured it out for NSTextView and his answer applies to iOS too. Turns out intrinsicContentSize doesn’t sync up with the layout manager. If a layout happens after intrinsicContentSize you could have a discrepancy.

He has an easy fix.

NSTextView in NSOutlineView with IntrinsicContentSize setting wrong height

Hex Bob-omb
  • 174
  • 2
  • 10
-1

Here are the steps

Solution works on all versions of iOS. Swift 3.0 and above.

  1. Add your UITextView to the View. I'm adding it with code, you can add via interface builder.
  2. Add constraints. I'm adding the constraints in code, you can do it in interface builder as well.
  3. Use UITextViewDelegate method func textViewDidChange(_ textView: UITextView) to adjust the size of the TextView

Code :

  1. //1. Add your UITextView in ViewDidLoad
    let textView = UITextView()
    textView.frame = CGRect(x: 0, y: 0, width: 200, height: 100)
    textView.backgroundColor = .lightGray
    textView.text = "Here is some default text."
    
    
    
    //2. Add constraints
    textView.translatesAutoresizingMaskIntoConstraints = false
    [
    textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
    textView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
    textView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
    textView.heightAnchor.constraint(equalToConstant: 50),
    textView.widthAnchor.constraint(equalToConstant: 30)
    ].forEach{ $0.isActive = true }
    
    textView.font = UIFont.preferredFont(forTextStyle: .headline)
    
    textView.delegate = self
    textView.isScrollEnabled = false
    
    textViewDidChange(textView)
    
    
    //3. Implement the delegate method.
    func textViewDidChange(_ textView: UITextView) {
    let size = CGSize(width: view.frame.width, height: .infinity)
    let estimatedSize = textView.sizeThatFits(size)
    
    textView.constraints.forEach { (constraint) in
        if constraint.firstAttribute == .height {
            print("Height: ", estimatedSize.height)
            constraint.constant = estimatedSize.height
        }
    }
    }
    
Vakas
  • 5,382
  • 3
  • 33
  • 44
-3

Not sure why people always over complicate things: here it is :

- (void)textViewDidChange:(UITextView *)textView{ CGRect frame = textView.frame;
CGFloat height = [self measureHeightOfUITextView:textView];
CGFloat insets = textView.textContainerInset.top + textView.textContainerInset.bottom;
height += insets;
frame.size.height = height;

if(frame.size.height > textView.frame.size.height){
    CGFloat diff = frame.size.height - textView.frame.size.height;
    textView.frame = CGRectMake(5, textView.frame.origin.y - diff, textView.frame.size.width, frame.size.height);
}
else if(frame.size.height < textView.frame.size.height){
    CGFloat diff = textView.frame.size.height - frame.size.height;
    textView.frame = CGRectMake(5, textView.frame.origin.y + diff, textView.frame.size.width, frame.size.height);
}
[textView setNeedsDisplay];
}
beryllium
  • 29,214
  • 15
  • 99
  • 123
Gal Blank
  • 1,987
  • 2
  • 15
  • 18