How do I size a UITextView to its content? - ios
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.
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;
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:
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+.
Very easy working solution using code and storyboard both.
By Code
textView.scrollEnabled = false
By Storyboard
Uncheck the Scrolling Enable
No need to do anything apart of this.
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:
Swift :
textView.sizeToFit()
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.
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;
}
}
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];
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.
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.
We can do it by constraints .
Set Height constraints for UITextView.
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 :)
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.
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:
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.
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;
}
The project deployment target must be iOS 8 or greater.
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:
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];
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..
The following things are enough:
Just remember to set scrolling enabled to NO for your UITextView:
Properly set Auto Layout Constraints.
You may even use UITableViewAutomaticDimension.
Using UITextViewDelegate is the easiest way:
func textViewDidChange(_ textView: UITextView) {
textView.sizeToFit()
textviewHeight.constant = textView.contentSize.height
}
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.
}
}
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;
}
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);
}
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
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.
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)
}
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
Hope this helps:
- (void)textViewDidChange:(UITextView *)textView {
CGSize textSize = textview.contentSize;
if (textSize != textView.frame.size)
textView.frame.size = textSize;
}
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];
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);
}
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.
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
}
}
}
Related
How to get UITextView height according to content in iOS 9
I'm using a UITextView to display some data. I need to calculate the height of the UITextView depending on its content. I have tried contentSize.height, but it doesn't work for me. Does anyone have a solution for this? Here is my code with AutoLayout. I have set the height constraint with outlet CGFloat height= _myTextView.contentSize.height; _heightConstraint.constant = height;
You could use the following to work out the height of the text, works perfectly for me: CGSize maximumTextViewSize = CGSizeMake(textView.frame.size.width, CGFLOAT_MAX); NSStringDrawingOptions options = NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin; NSDictionary *attr = #{NSFontAttributeName: [your font and font size]}; CGRect textViewBounds = [theText boundingRectWithSize:maximumTextViewSize options:options attributes:attr context:nil]; CGFloat height = ceilf(textViewBounds.size.height);
Just add [self layoutIfNeeded] After you've calculated the height, and you should be alright. If not, come back to me.
func heightForView(text:String, font:UIFont, width:CGFloat, xpos:CGFloat) -> CGFloat { let label:UILabel = UILabel(frame: CGRectMake(xpos, 0, width, CGFloat.max)) label.numberOfLines = 0 label.lineBreakMode = NSLineBreakMode.ByWordWrapping label.font = font label.text = text label.sizeToFit() return label.frame.height }
First size the text view to the content with: [yourtTextView sizetofit]; Then get the height of the view with: double textViewHeight = yourtTextView .frame.size.height;
The textView is not aware of its content when the text view scroll is enabeld change that in the storyboard by unchecking the Scrolling enables or by code by adding this: textview.scrollEnabled = false this will make the textView aware of its content and it will grow or shrink in height based on its content.
UITextView is always on one line, attempting to resize [duplicate]
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.
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;
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: 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+.
Very easy working solution using code and storyboard both. By Code textView.scrollEnabled = false By Storyboard Uncheck the Scrolling Enable No need to do anything apart of this.
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:
Swift : textView.sizeToFit()
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.
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; } }
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];
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.
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.
We can do it by constraints . Set Height constraints for UITextView. 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 :)
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.
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: 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. 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; } The project deployment target must be iOS 8 or greater.
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:
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];
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..
The following things are enough: Just remember to set scrolling enabled to NO for your UITextView: Properly set Auto Layout Constraints. You may even use UITableViewAutomaticDimension.
Using UITextViewDelegate is the easiest way: func textViewDidChange(_ textView: UITextView) { textView.sizeToFit() textviewHeight.constant = textView.contentSize.height }
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. } }
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; }
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); }
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
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.
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) }
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
Hope this helps: - (void)textViewDidChange:(UITextView *)textView { CGSize textSize = textview.contentSize; if (textSize != textView.frame.size) textView.frame.size = textSize; }
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];
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); }
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.
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 } } }
UILabel text alignment issues in iphone?
I am getting JSON response from server and set that contents to UILabel and sometimes the text will be one line and sometimes the text will be 10 lines.So i display this UILabel inside the UIScrollView. But i got the empty space in UILabel This is my screenshots.. Currently i am using storyboard and auto layout So i want to avoid these empty space in UILabel. Please give me suggestions for this alignment issues.?? Code for changing UILabel height but this is not working for me CGRect textRect = [strDetailsFinal boundingRectWithSize:CGSizeMake(296, FLT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName: labDetails.font} context:nil]; CGSize size = textRect.size; CGRect newFrame = labDetails.frame; newFrame.size.height = size.height; labDetails.frame = CGRectMake(160, 200, 300, newFrame.size.height);
May this help you NSDictionary *attributesDic= [NSDictionary dictionaryWithObjectsAndKeys: [UIFont fontWithName:#"FontName" size:15], NSFontAttributeName,nil]; CGRect frame = [label.text boundingRectWithSize:CGSizeMake(263, 2000.0) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributesDic context:nil]; CGSize size = frame.size; and its possible answer Dynamically resize label in iOS 7 or you can try this https://gist.github.com/danielphillips/1005520
Use [labDetails sizeToFit]. If you have size constraints then use [sizeThatFits:CGSizeMake(WidthConstraint, HeightConstraint)] then use the result of that to set your label frame.
why don't you use UIButton instead of UILabel. UIButton has property to align the text vertically, while UILabel doesn't. Use UIButton with userInteractionEnabled = NO. Check my this reply here: Vertically align UILabel
Remember one thumb rule - You should not set frame when using autolayout - You are going against Autolayout basic concept - Instead try to autolayout constraints programatically in your case -(void)awakeFromNib{ [super awakeFromNib]; for(NSLayoutConstraint *cellConstraint in self.constraints){ [self removeConstraint:cellConstraint]; id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem; id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem; NSLayoutConstraint* contentViewConstraint = [NSLayoutConstraint constraintWithItem:firstItem attribute:cellConstraint.firstAttribute relatedBy:cellConstraint.relation toItem:seccondItem attribute:cellConstraint.secondAttribute multiplier:cellConstraint.multiplier constant:cellConstraint.constant]; [self.contentView addConstraint:contentViewConstraint]; } }
You have taken a fix size UILable rather you have to use a dynamic size UILable. Before that you need to calculate the size of text/string #define FONT_SIZE 15.0f #define LABEL_CONTENT_WIDTH 320.0f #define LABEL_CONTENT_MARGIN 10.0f NSString *text = #"YOUR TEXT HERE"; CGSize constraint = CGSizeMake(LABEL_CONTENT_WIDTH - (LABEL_CONTENT_MARGIN * 2), 20000.0f); CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping]; CGFloat height = MAX(size.height, 44.0f); after calculating the size, apply them to your label like below [labDetails setFrame:CGRectMake(labDetails.frame.origin.x, labDetails.frame.origin.y, labDetails.frame.size.width, MAX(height, 44.0f))]; And If you are using autolayout option then please check here: Link
Use Upper label frame's origin and size, and based on that set your detail labels frame, Use sizeToFit property of label. Suppose 582,618 is your title label, Like, labDetails.frame=CGRectMake(5, title.frame.origin.x+title.frame.size.height+5, 300, 100); [labDetails sizeToFit];
Labels and TextFields text clipped with specific font only on iOS6
I am writing an app that uses 3 fonts for 2 language UIs. For hebrew UI with hebrew font all is good, but for the english UI all the labels are getting clipped at the bottom with the english font on iOS6 (on 7 it's good). I started investigating, and realised that also the line spacing in more than 1 line texts is way too small, and sizeToFit, sizeThatFits: and constrainToSize all gives height that is way too small for that font to fit in the frame. First I tried to set clipsToBounds:NO which did nothing, the I thought about hardcode enlarging the frame by *1.15 when using the eng font, but than 1) I still have the line spacing problem, and 2) I couldn't get it to work with the UITextFields (For some reason the placeholder and the caret both getting clipped but typed text works fine...) - I tried overriding textRectForBounds: and editingRectForBounds: and return the given bounds with (0,10,0,10) for UIEdgeInsets but no luck... I thought about editing the font info itself, but A I have no idea how to do it (And couldn't find any info on Google) and B couldn't find any mac app that will do it (Only Windows stuff...). Also - It seems weird that I need to do it because on iOS7 it works perfectly without any need to adjust any frame... (I Tested this on iOS6 & iOS7 simulators AND on iOS6 iPhone 5 device AND iOS7 iPhone5 device...). Typing text on iOS7: Placeholder iOS7: Typing text on iOS6.1: Placeholder on iOS6.1: Some other place with UILabel that's clipped: (This case on button, but happens everywhere) Again: This only happens on iOS6.1 and not on 7, and my guess is it has something to do with the way the SDK reads the font size in pixels from the font file that has maybe somehow changed from ios6 to 7... EDIT: This is the work-around tryout I did for the UITextField problem: - (CGRect) textRectForBounds:(CGRect)bounds { return [self UIEdgeInsetsInsetRectForTextFieldBounds:bounds]; } - (CGRect)editingRectForBounds:(CGRect)bounds { return [self UIEdgeInsetsInsetRectForTextFieldBounds:bounds]; } - (CGRect)UIEdgeInsetsInsetRectForTextFieldBounds:(CGRect)bounds { bounds = CGRectMake(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height + 5); return UIEdgeInsetsInsetRect(bounds, UIEdgeInsetsMake(-2, 10, -2, 10)); } Note: It's written on a UITextField category. I know it's not the way to go but it does change the insets so I know the code happens and makes an influence, it just doesn't change the farm size for the placeholder as expected. Didn't work... Also these are all the tryouts I did for fixing the labels with actually calculating the space needed, as you can see by the comment mark I ended up using a hard-coded-workaround: (The last paragraph commented is my try to work-around the line spacing problem - also didn't work) // CGSize maxSize = CGSizeMake(self.frame.size.width, MAXFLOAT); //// CGSize expectedLabelSize = [self.text sizeWithFont:self.font constrainedToSize:maxSize lineBreakMode:self.lineBreakMode]; //// CGRect newFrame = self.frame; //// newFrame.size.height = expectedLabelSize.height*2; //// self.frame = newFrame; //// [self setClipsToBounds:NO]; // // CGSize expectedLabelSize = [self.text sizeWithFont:self.font constrainedToSize:maxSize lineBreakMode:NSLineBreakByTruncatingTail]; // // //adjust the label the the new height. // CGRect newFrame = self.frame; // newFrame.size.height = expectedLabelSize.height; // self.frame = newFrame; // [self setLineBreakMode:NSLineBreakByWordWrapping]; // [self setNumberOfLines:1]; // [self sizeToFit]; // CGSize maxSize = CGSizeMake(self.frame.size.width, CGFLOAT_MAX); // CGSize requiredSize = [self sizeThatFits:maxSize]; // self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, requiredSize.width, requiredSize.height); [self changeFrameHeightTo:self.frame.size.height*1.17]; [self changeFrameYBy:-5]; // NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:self.text]; // NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; // [paragraphStyle setLineSpacing:10]; // [attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [self.text length])]; // self.attributedText = attributedString; **changeFrame... is a category I built for UIView for changing specific values without having to use setFrame:CGRectMake(...*
Why not calculate the text size according to the text and font you use? CGSize titleSize = [title sizeWithFont:<your font here> constrainedToSize:<button size > lineBreakMode:NSLineBreakByTruncatingTail]; then you can change the size of the placeholder accordingly. Note, this function is deprecated in ios 7.0. for ios7 you need to use: boundingRectWithSize:options:attributes:context:
UITextView content size different in iOS7
I am using an UITextView that will be expandable by taping a "more" button. The problem is the following: On iOS6 I use this, self.DescriptionTextView.text = #"loong string"; if(self.DescriptionTextView.contentSize.height>self.DescriptionTextView.frame.size.height) { //set up the more button } The problem is that on iOS7 the contentSize.height returns a different value (far smaller) than the value it returns on iOS6. Why is this? How to fix it?
The content size property no longer works as it did on iOS 6. Using sizeToFit as others suggest may or may not work depending on a number of factors. It didn't work for me, so I use this instead: - (CGFloat)measureHeightOfUITextView:(UITextView *)textView { if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) { // This is the code for iOS 7. contentSize no longer returns the correct value, so // we have to calculate it. // // This is partly borrowed from HPGrowingTextView, but I've replaced the // magic fudge factors with the calculated values (having worked out where // they came from) CGRect frame = textView.bounds; // Take account of the padding added around the text. UIEdgeInsets textContainerInsets = textView.textContainerInset; UIEdgeInsets contentInsets = textView.contentInset; CGFloat leftRightPadding = textContainerInsets.left + textContainerInsets.right + textView.textContainer.lineFragmentPadding * 2 + contentInsets.left + contentInsets.right; CGFloat topBottomPadding = textContainerInsets.top + textContainerInsets.bottom + contentInsets.top + contentInsets.bottom; frame.size.width -= leftRightPadding; frame.size.height -= topBottomPadding; NSString *textToMeasure = textView.text; if ([textToMeasure hasSuffix:#"\n"]) { textToMeasure = [NSString stringWithFormat:#"%#-", textView.text]; } // NSString class method: boundingRectWithSize:options:attributes:context is // available only on ios7.0 sdk. NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; [paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping]; NSDictionary *attributes = #{ NSFontAttributeName: textView.font, NSParagraphStyleAttributeName : paragraphStyle }; CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil]; CGFloat measuredHeight = ceilf(CGRectGetHeight(size) + topBottomPadding); return measuredHeight; } else { return textView.contentSize.height; } }
Try the answer in the following link, layoutIfNeeded should be called before contentSize. iOS7 UITextView contentsize.height alternative The answer: In iOS7, UITextView uses NSLayoutManager to layout text: // If YES, then the layout manager may perform glyph generation and layout for a given portion of the text, without having glyphs or layout for preceding portions. The default is NO. Turning this setting on will significantly alter which portions of the text will have glyph generation or layout performed when a given generation-causing method is invoked. It also gives significant performance benefits, especially for large documents. #property(NS_NONATOMIC_IOSONLY) BOOL allowsNonContiguousLayout; disable allowsNonContiguousLayout to fix contentSize : textView.layoutManager.allowsNonContiguousLayout = NO;
This link seems to have the answer. You must use sizeToFit before using contentSize.
Try with following code, Its will be work in both iOS6 and 7, please try it. CGSize myTextViewSize = [self.myTextView sizeThatFits:CGSizeMake(self.myTextView.frame.size.width, FLT_MAX)]; self.myTextView.height = myTextViewSize.height; NSLog(#"%f", self.myTextView.height);