2

I'm using iOS7.

I want to change the height of my UITextView dynamically according to the content height. However, I find in iOS7, the contentSize is a mess. For example, when I delete a line, the contentSize will not change, but I expect its height to decrease.

So I write the following

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    //  increase a line
    if ([text isEqualToString:@"\n"]) {
        _FRAME_SET_H(textView, textView.contentSize.height + 24.0f);

    //  decrease a line
    } else if (range.length == 1 && [textView.text hasSuffix:@"\n"]) {
        _FRAME_SET_H(textView, textView.contentSize.height - 24.0f);

    } else {
        _FRAME_SET_H(textView, textView.contentSize.height);
    }

    return YES;
}

Everything works fine, until I input a very long text:

enter image description here

The contentSize will not change when the text is long enough to cause a automatically line change!

Could anyone help me?

What I want is simple: change the height of the UITextView according to its content. However, I have spent more than two days on it, but still not solved.

The UITextView in iOS7 is so bug.


I tried to follow Uptown Apps's solution. Following is my new code (changed a bit to fit in my other project):

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    return YES;
}

- (void)textViewDidChange:(UITextView *)textView {
    CGRect rect = [textView.text
                   boundingRectWithSize:CGSizeMake(300, CGFLOAT_MAX)
                   options:NSStringDrawingUsesLineFragmentOrigin
                   attributes:@{NSFontAttributeName: textView.font}
                   context:nil];
    _FRAME_SET_H(textView, rect.size.height);

    _FRAME_SET_H(self, _FRAME_H(textView) + _BLOCK_PADDING * 2);
}

However, trouble remains. In the following image, the white rect is the UITextView, and the black background is its superview.

When I start the app, the UITextView's height is 0, and the screen looks like: enter image description here

Then I type 'A', the screen turns into: enter image description here

You see there is two problems: 1) the height is not enough. 2) the caret is strange!

Then I type some more 'a', the screen turns into: enter image description here

Then I type a 'return' to change a line, the screen turns into: enter image description here

Yes, it does not change! But I expect it to increase its height to include the new blank line.

Then I type a 'A', the screen turns into: enter image description here

The height is increased, but the new text is not showing, neither is the caret. I guess it is also due to that the height is not enough.

Then I type another 'a', the screen turns into: enter image description here

Letters appear, but only a half...

By the way, when I use UILabel, I can get the correct text height using this method!!!! (i.e. calling boundingRectWithSize:)

HanXu
  • 5,507
  • 6
  • 52
  • 76

3 Answers3

5

--Edit--

See How do I size a UITextView to its content?

Once the text has changed, you can go back to your original solution of using the contentSize. I adjusted this code to use a maxHeight

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    return YES;
}

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

--Original Answer--

Have you tried waiting until the text has changed and then use the NSString methods to get the size? I haven't tested this but it should get you started in the right direction.

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    return YES;
}

- (void)textViewDidChange:(UITextView *)textView{

    CGFloat myMaxSize = 568.0f; // Set to max height you would ever want your textView to expand to

    CGRect frameRect = textView.frame;
    CGRect rect = [textView.text 
boundingRectWithSize:CGSizeMake(frameRect.size.width, CGFLOAT_MAX)
             options:NSStringDrawingUsesLineFragmentOrigin 
          attributes:@{NSFontAttributeName: textView.font} 
             context:nil];

    CGSize size = rect.size;
    if (size.height > myMaxSize) {
        size.height = myMaxSize;
    }

    frameRect.size = size;
    [textView setFrame:frameRect];

}
Community
  • 1
  • 1
Uptown Apps
  • 707
  • 1
  • 5
  • 11
  • I think you may have a bug in your _FRAME_SET_H function. Are you trying to expand the textview upwards (likes Messages app)? My code doesn't do that but could be adapted to do it. – Uptown Apps Nov 17 '13 at 03:05
  • Wow, I should have seen this sooner. Once you've let the text change, you can use the content size like you originally wanted to. See http://stackoverflow.com/questions/50467/how-do-i-size-a-uitextview-to-its-content The only thing you'd need to account for is a max height and scrolling the view when you hit the max height and enter a newline character (return) but haven't entered new text. – Uptown Apps Nov 17 '13 at 03:53
  • Interestingly! Your new solution works! But the original one does not, i.e. boundingRectWithSize: does not work...strange..anyway, thank you!!! – HanXu Nov 17 '13 at 10:39
0

You can get the correct height of the text by this function:

CGSize textSize = [text sizeWithFont:[UIFont systemFontOfSize:13.0f] constrainedToSize:CGSizeMake(textView.width, 20000) lineBreakMode: NSLineBreakByWordWrapping];

float text_height = textSize.height;

This function constrain the text in the width of your textView. So in according of the width, it return the height of the text in that space.

Let me know!

Matteo Gobbi
  • 17,697
  • 3
  • 27
  • 41
  • `sizeWithFont:constrainedToSize:lineBreakMode:` is deprecated as of iOS7. You should now use `boundingRectWithSize:options:attributes:context:` per my answer. – Uptown Apps Nov 17 '13 at 02:30
0

You can change the height of the text view dynamically, using:

[textView sizeToFit];

But the problem is that, if the content inside the text view is too little, it will resize to smaller than the original size. Here is the best solution for this:

- (void)textViewDidChange:(UITextView *)textView{
    CGRect frameForTextView=textView.frame;
    [textView sizeToFit];
    if (textView.frame.size.height < frameForTextView.size.height) {
        textView.frame=frameForComments;
    }
    else
    {
        //Do other UI adjustment here when height of textView increased than default size      
    }
}
Vivek Deore
  • 71
  • 10