Here's the code for a UITextView that I want to size to the height of its content.
If I write the textView.frame height explicitly like:
textView.frame = CGRectMake(100, 12, 320, 458);
the textView sizes to it's content as expected.
If, however, I write it like the following. It doesn't even display although the NSLog statement says that there's a value to textView.contentSize.height
UITextView *textView = [[UITextView alloc] init];
textView.layer.borderWidth = 5.0f;
textView.layer.borderColor = [[UIColor redColor] CGColor];
textView.text = [item objectForKey:#"description"];
textView.frame = CGRectMake(100, 12, 320, textView.contentSize.height);
NSLog(#"%f textviewcontnet size", textView.contentSize.height);
textView.editable = NO;
[self.view addSubview:textView];
When I log the output of:
NSLog(#"%f textviewcontent size", textView.contentSize.height);
I get "458.000000 textviewcontent size"
thanks for any help
I'd suggest trying:
UITextView *textView = [[UITextView alloc] init];
textView.layer.borderWidth = 5.0f;
textView.layer.borderColor = [[UIColor redColor] CGColor];
textView.text = [item objectForKey:#"description"];
textView.frame = CGRectMake(100, 12, 320, 458);
textView.editable = NO;
[self.view addSubview:textView];
textView.frame = CGRectMake(100, 12, 320, textView.contentSize.height);
I've heard that textView.contentSize.height doesn't work until it's been added to a view (though that's not my experience). More importantly, I don't know how it would interpret textView.contentSize.height if it doesn't yet know what the width of the control is. So go ahead, set the initial frame, do addSubview and then readjust the size based upon textView.contentSize.height.
Quickly copied out of one of my projects:
AppDelegate *appDelegate;
CGSize textSize1, textSize2;
appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
textSize1 = [self.subjectLabel.text sizeWithFont:[appDelegate fontNormal] constrainedToSize:CGSizeMake(300.0f, 10000.0f) lineBreakMode:NSLineBreakByWordWrapping];
self.subjectLabel.frame = CGRectMake(10, 5, 300, textSize1.height);
textSize2 = [self.descriptionLabel.text sizeWithFont:[appDelegate fontNormal] constrainedToSize:CGSizeMake(300.0f, 10000.0f) lineBreakMode:NSLineBreakByWordWrapping];
self.descriptionLabel.frame = CGRectMake(10, 5 + textSize1.height + 5, 300, textSize2.height);
[appDelegate fontNormal] just returns a UIFont object, the one that I am using for all "normal" text items. Don't worry about that too much. But it is important that you use the same font that is used for the text view too.
My example is a bit easier because it is a UILable. That works with a text view too but you will have to consider the insects. Easy solution, just substract some "fuzzy offset" from the width compared to the frame width of your text view.
Related
How can I create a horizontal scrolling UITextView?
When I set my text programmatically it adds automatic line breaks, so I can only scroll vertically...
titleView.text = #"this is a very long text. this is a very long text. this is a very long text. this is a very long text. this is a very long text.";
Thanks for your answers.
EDIT:
So far I tried this:
UIScrollview *yourScrollview = [[UIScrollView alloc] initWithFrame:CGRectMake(0 ,0 , self.view.frame.size.width, 50)];
CGFloat textLength = [titleView.text sizeWithFont:titleView.font constrainedToSize:CGSizeMake(9999, 50) lineBreakMode:NSLineBreakByWordWrapping].width;
yourScrollview.contentSize = CGSizeMake(textLength + 200, 500); //or some value you like, you may have to try this out a few times
titleView.frame = CGRectMake(titleView.frame.origin.x, titleView.frame.origin.y, textLength, titleView.frame.size.height);
[yourScrollview addSubview: titleView];
NSLog(#"%f", textLength);
but I received: 'Threat 1: signal SIGABRT'
I have not yet done something like this, but I would try the following steps to accomplish this:
Create a UIScrollview *yourScrollview = [[UIScrollView alloc] initWithFrame:CGRectMake(0 ,0 , self.view.frame.size.width, 50)]; //
Use CGFloat textLength = [titleView.text sizeWithFont:titleView.font constrainedToSize:CGSizeMake(9999, 50) lineBreakMode:NSLineBreakByWordWrapping].width;
to get the final length of your text
Set yourScrollView.contentSize = CGSizeMake(textLength + 20, 50); //or some value you like, you may have to try this out a few times
Also set titleTextView.frame = CGRectMake(titleTextView.frame.origin.x, titleTextView.frame.origin.y, textLength, titleTextView.frame.size.height);
Make titleView a subview of yourScrollView: [yourScrollView addSubview: titleView];
Hope this gives you a good start!
EDIT: This Code will work:
Please notice I used a UILabel instead of a UITextView.
UILabel *titleView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 40)];
titleView.text = #"this is a very long text. this is a very long text. this is a very long text. this is a very long text. this is a very long text.";
titleView.font = [UIFont systemFontOfSize:18];
titleView.backgroundColor = [UIColor clearColor];
titleView.numberOfLines = 1;
UIScrollView *myScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 50)];
CGFloat textLength = [titleView.text sizeWithFont:titleView.font constrainedToSize:CGSizeMake(9999, 50) lineBreakMode:NSLineBreakByWordWrapping].width;
myScrollView.contentSize = CGSizeMake(textLength + 20, 50); //or some value you like, you may have to try this out a few times
titleView.frame = CGRectMake(titleView.frame.origin.x, titleView.frame.origin.y, textLength, titleView.frame.size.height);
[myScrollView addSubview: titleView];
[self.view addSubview:myScrollView];
[titleView release];
[myScrollView release];
textViewBusiness = [[UITextView alloc] initWithFrame:CGRectMake(25,332,268,60)];
textViewBusiness.text=strMyBusiness;
textViewBusiness.editable=NO;
textViewBusiness.font = [UIFont fontWithName:#"Arial" size: 17.0];
textViewBusiness.layer.borderWidth = 2.0f;
textViewBusiness.layer.borderColor = [[UIColor grayColor] CGColor];
textViewBusiness.backgroundColor = [UIColor clearColor];
[textViewBusiness setTextColor:[UIColor blackColor]];
[self.scrollView addSubview: textViewBusiness];
CGRect frame = textViewBusiness.frame;
frame.size.height = textViewBusiness.contentSize.height;
textViewBusiness.frame = frame;
with the increase in contents i want to increase the size of text field....
This code is not working for me ...
Thanks
You don't need to take UITextView for showing un-editable text. Besides this you can use UILabel and you can find out the height of label at run time. Each time the content vary in size set frame of UILabel accordingly.
Use this to find out height of your label at run time
- (CGFloat) heightOfTextLabel:(NSString *) contentText
{
CGSize constraint = CGSizeMake(268,4000);
CGSize size = [contentText sizeWithFont:[UIFont fontWithName:#"Arial" size: 17.0] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
return size.height;
}
This method would return you a variable height of the content each time.
Now set this height to your UILabel
CGFloat heightOfLabel = [self heightOfTextLabel:strMyBusiness];
UILabel* textToShowLabel = [[UILabel alloc] initWithFrame:CGRectMake(25,332,268,heightOfLabel)];
textToShowLabel.text=strMyBusiness;
textToShowLabel.font = [UIFont fontWithName:#"Arial" size: 17.0];
textToShowLabel.layer.borderWidth = 2.0f;
textToShowLabel.layer.borderColor = [[UIColor grayColor] CGColor];
textToShowLabel.backgroundColor = [UIColor clearColor];
[textToShowLabel setTextColor:[UIColor blackColor]];
[self.scrollView addSubview: textToShowLabel];
You need to divide your code into 2 fragments, then place them into proper places, then your code should work.
Fragment 1 (in my test, I place this fragment in viewDidLoad):
textViewBusiness = [[UITextView alloc] initWithFrame:CGRectMake(25,332,268,60)];
textViewBusiness.text=strMyBusiness;
textViewBusiness.editable=NO;
textViewBusiness.font = [UIFont fontWithName:#"Arial" size: 17.0];
textViewBusiness.layer.borderWidth = 2.0f;
textViewBusiness.layer.borderColor = [[UIColor grayColor] CGColor];
textViewBusiness.backgroundColor = [UIColor clearColor];
[textViewBusiness setTextColor:[UIColor blackColor]];
[self.scrollView addSubview: textViewBusiness];
Ensure that the above fragment run, and your text view is displayed in the screen, then run the second fragment. If your text view is not displayed in the screen, then the contentView is not initialized, and the height is undefined.
Fragment 2 (in my test, I place this fragment in viewDidAppear):
CGRect frame = textViewBusiness.frame;
frame.size.height = textViewBusiness.contentSize.height;
textViewBusiness.frame = frame;
Good luck!
GrowingTextView is a reusable iOS compenent that does exactly what you need, you can find it on GitHub here .
I hope this helps.
Am working in an iPhone app using UITextView. I am trying to increase the height of UITextView based on it's content length. It is working fine. But, when the height of uitextview increase the UITextView should change the height in up side not increase height in down.
This is the code am trying in my project,
baseView = [[UIView alloc] initWithFrame:CGRectMake(0, 50, 320, 50)];
baseView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:baseView];
TextView = [[UITextView alloc] initWithFrame:CGRectMake(20, 10, 280, 40)];
TextView.backgroundColor = [UIColor whiteColor];
TextView.layer.borderWidth = 2;
TextView.delegate = self;
TextView.font = [UIFont fontWithName:#"Helvetica" size:16];
[baseView addSubview: TextView];
-(void)textViewDidChange:(UITextView *)textView
{
CGRect frame = messageTextView.frame;
frame.size.height = messageTextView.contentSize.height;
TextView.frame = CGRectMake(20, 10, 280, frame.size.height);
baseView.frame = CGRectMake(0, 50, 320, frame.size.height+20);
}
When the UITextView height is changing the effect textview height will be increase on top side. Can anyone please help me on this? Thanks in advance.
In the method you change the frames, also edit the frame.origin.y value of the textView
-(void)textViewDidChange:(UITextView *)textView
{
CGRect frame = messageTextView.frame;
frame.size.height = messageTextView.contentSize.height;
TextView.frame = CGRectMake(20, 0, 280, frame.size.height); //previous y was 10
baseView.frame = CGRectMake(0, 50, 320, frame.size.height+20);
}
Nonetheless, I believe you are aware that you have only 10 pixels for the textView to go up, as you are adding it to a BaseView view.
I am custom drawing some text:
point = CGPointMake(77, 5);
[[message valueForKey:#"user_login"] drawAtPoint:point forWidth:200
withFont:mainFont
minFontSize:MIN_MAIN_FONT_SIZE
actualFontSize:NULL
lineBreakMode:UILineBreakModeTailTruncation
baselineAdjustment:UIBaselineAdjustmentAlignBaselines];
How can I make it draw 5 lines? Equivalent to:
rect = CGRectMake(77, 25, 238, 68);
bodyLabel = [[UILabel alloc] initWithFrame:rect];
bodyLabel.font = [UIFont fontWithName:#"HelveticaNeue" size:12];
bodyLabel.numberOfLines = 5;
bodyLabel.lineBreakMode = UILineBreakModeWordWrap;
bodyLabel.textColor = [UIColor blackColor];
[self.contentView addSubview: bodyLabel];
The documentation for -drawAtPoint:withFont:... says "This method does not perform any line wrapping during drawing." If you use -drawInRect:withFont: instead of -drawAtPoint:withFont:..., then it will draw multiple lines. You can also use -sizeWithFont:constrainedToSize: to figure out what the size will be.
Instead of deprecated drawInRect:withFont:... in iOS7+ you should use drawWithRect:options:attributes:context:
[string drawWithRect:CGRectMake(x, y, width, height)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine
attributes:#{NSFontAttributeName:<font>, NSForegroundColorAttributeName:<color>
context:nil];
I have a UITextView added on my UIView. The textview added is not editable, it is just to display some data. The data displayed in the textview is dynamic. Thats is the number of lines is not fixed. It may vary. So if the number of line increases, the size of the textview also needs to be increased. I have no clue how to do this. Please give me some ideas.
UPDATE:
Here's what I'm doing:
UIView *baseView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 200)];
baseView.backgroundColor = [UIColor grayColor];
[window addSubview:baseView];
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(5, 30, 100, 30)];
textView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
textView.text = #"asdf askjalskjalksjlakjslkasj";
[textView sizeToFit];
[baseView addSubview:textView];
There is an answer posted at How do I size a UITextView to its content?
CGRect frame = _textView.frame;
frame.size.height = _textView.contentSize.height;
_textView.frame = frame;
or better(taking into account contentInset thanks to kpower's comment)
CGRect frame = _textView.frame;
UIEdgeInsets inset = textView.contentInset;
frame.size.height = _textView.contentSize.height + inset.top + inset.bottom;
_textView.frame = frame;
note: If you are going to reference a property of an object many times(e.g. frame or contentInset) it's better to assign it to a local variable so you don't trigger extra method calls(_textView.frame/[_textView frame] are method calls). If you are calling this code a lot(100000s of times) then this will be noticeably slower(a dozen or so method calls is insignificant).
However... if you want to do this in one line without extra variables it would be
_textView.frame = CGRectMake(_textView.frame.origin.x, _textView.frame.origin.y, _textView.frame.size.width, _textView.contentSize.height + _textView.contentInset.top + _textView.contentInset.bottom);
at the expense of 5 extra method calls.
You can use setFrame: or sizeToFit.
UPDATE:
I use sizeToFit with UILabel, and it works just fine, but UITextView is a subclass of UIScrollView, so I can understand why sizeToFit doesn't produce the desired result.
You can still calculate the text height and use setFrame, but you might want to take advantage of UITextView's scrollbars if the text is too long.
Here's how you get the text height:
#define MAX_HEIGHT 2000
NSString *foo = #"Lorem ipsum dolor sit amet.";
CGSize size = [foo sizeWithFont:[UIFont systemFontOfSize:14]
constrainedToSize:CGSizeMake(100, MAX_HEIGHT)
lineBreakMode:UILineBreakModeWordWrap];
and then you can use this with your UITextView:
[textView setFont:[UIFont systemFontOfSize:14]];
[textView setFrame:CGRectMake(5, 30, 100, size.height + 10)];
or you can do the height calculation first and avoid the setFrame line:
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(5, 30, 100, size.height + 10)];
sizeToFit Does Work
If you call sizeToFit after you set the text the first time it resizes. So after the first time you set it subsequent calls to set text will result in no change in size. Even if you call sizeToFit.
However, you can force it to resize like this:
Set the text.
Change the textView frame height to be CGFLOAT_MAX.
Call sizeToFit.
textView.contentSize.height in the textViewDidChange can only resize after text actually grows. For best visual result better to resize beforehand. After several hours I've figured out how to make it the same perfectly as in Instagram (it has the best algorithm among all BTW)
Initialize with this:
// Input
_inputBackgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, size.height - _InputBarHeight, size.width, _InputBarHeight)];
_inputBackgroundView.autoresizingMask = UIViewAutoresizingNone;
_inputBackgroundView.contentMode = UIViewContentModeScaleToFill;
_inputBackgroundView.userInteractionEnabled = YES;
[self addSubview:_inputBackgroundView];
[_inputBackgroundView release];
[_inputBackgroundView setImage:[[UIImage imageNamed:#"Footer_BG.png"] stretchableImageWithLeftCapWidth:80 topCapHeight:25]];
// Text field
_textField = [[UITextView alloc] initWithFrame:CGRectMake(70.0f, 0, 185, 0)];
_textField.backgroundColor = [UIColor clearColor];
_textField.delegate = self;
_textField.contentInset = UIEdgeInsetsMake(-4, -2, -4, 0);
_textField.showsVerticalScrollIndicator = NO;
_textField.showsHorizontalScrollIndicator = NO;
_textField.font = [UIFont systemFontOfSize:15.0f];
[_inputBackgroundView addSubview:_textField];
[_textField release];
[self adjustTextInputHeightForText:#""];
Fill UITextView delegate methods:
- (void) textViewDidBeginEditing:(UITextView*)textView {
[self adjustTextInputHeightForText:_textField.text];
}
- (void) textViewDidEndEditing:(UITextView*)textView {
[self adjustTextInputHeightForText:_textField.text];
}
- (BOOL) textView:(UITextView*)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text {
if ([text isEqualToString:#"\n"])
{
[self performSelector:#selector(inputComplete:) withObject:nil afterDelay:.1];
return NO;
}
else if (text.length > 0)
{
[self adjustTextInputHeightForText:[NSString stringWithFormat:#"%#%#", _textField.text, text]];
}
return YES;
}
- (void) textViewDidChange:(UITextView*)textView {
[self adjustTextInputHeightForText:_textField.text];
}
And the trick is...
- (void) adjustTextInputHeightForText:(NSString*)text {
int h1 = [text sizeWithFont:_textField.font].height;
int h2 = [text sizeWithFont:_textField.font constrainedToSize:CGSizeMake(_textField.frame.size.width - 16, 170.0f) lineBreakMode:UILineBreakModeWordWrap].height;
[UIView animateWithDuration:.1f animations:^
{
if (h2 == h1)
{
_inputBackgroundView.frame = CGRectMake(0.0f, self.frame.size.height - _InputBarHeight, self.frame.size.width, _InputBarHeight);
}
else
{
CGSize size = CGSizeMake(_textField.frame.size.width, h2 + 24);
_inputBackgroundView.frame = CGRectMake(0.0f, self.frame.size.height - size.height, self.frame.size.width, size.height);
}
CGRect r = _textField.frame;
r.origin.y = 12;
r.size.height = _inputBackgroundView.frame.size.height - 18;
_textField.frame = r;
} completion:^(BOOL finished)
{
//
}];
}
This works perfectly for me:
#define MAX_HEIGHT 2000
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:14]
constrainedToSize:CGSizeMake(100, MAX_HEIGHT)
lineBreakMode:UILineBreakModeWordWrap];
[textview setFont:[UIFont systemFontOfSize:14]];
[textview setFrame:CGRectMake(45, 6, 100, size.height + 10)];
textview.text = text;
Do the following:
_textView.text = someText;
[_textView sizeToFit];
_textView.frame.height = _textView.contentSize.height;
Addressing the similar issue I just created a an auto-layout based light-weight UITextView subclass which automatically grows and shrinks based on the size of user input and can be constrained by maximal and minimal height - all without a single line of code.
https://github.com/MatejBalantic/MBAutoGrowingTextView
The answer given by #Gabe doesn't work in iOS7.1 seemingly until after viewDidAppear. See my tests below.
UPDATE: Actually, the situation is even more complicated. If you assign textView.text in the resizeTheTextView method, in iOS7, the resizing amounts to allowing for only a single line of text. Seriously odd.
UPDATE2: See also UITextView content size different in iOS7
UPDATE3: See my code at the very bottom for what I'm using now. Seems to do the job.
#import "ViewController.h"
#interface ViewController ()
{
UITextView *textView;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
textView = [[UITextView alloc] initWithFrame:CGRectMake(50, 50, 200, 1)];
[self.view addSubview:textView];
CALayer *layer = textView.layer;
layer.borderColor = [UIColor blackColor].CGColor;
layer.borderWidth = 1;
textView.text = #"hello world\n\n";
// Calling the method directly, after the view is rendered, i.e., after viewDidAppear, works on both iOS6.1 and iOS7.1
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:#"Change size" forState:UIControlStateNormal];
[button addTarget:self action:#selector(resizeTheTextView) forControlEvents:UIControlEventTouchUpInside];
[button sizeToFit];
CGRect frame = button.frame;
frame.origin.y = 400;
button.frame = frame;
[self.view addSubview:button];
// Works on iOS6.1, but does not work on iOS7.1
//[self resizeTheTextView];
}
- (void) viewWillAppear:(BOOL)animated
{
// Does not work on iOS7.1, but does work on iOS6.1
//[self resizeTheTextView];
}
- (void) viewDidAppear:(BOOL)animated
{
// Does work on iOS6.1 and iOS7.1
//[self resizeTheTextView];
}
- (void) resizeTheTextView
{
NSLog(#"textView.frame.size.height: %f", textView.frame.size.height);
NSLog(#"textView.contentSize.height: %f", textView.contentSize.height);
// 5) From https://stackoverflow.com/questions/728704/resizing-uitextview
CGRect frame = textView.frame;
UIEdgeInsets inset = textView.contentInset;
frame.size.height = textView.contentSize.height + inset.top + inset.bottom;
textView.frame = frame;
NSLog(#"inset.top: %f, inset.bottom: %f", inset.top, inset.bottom);
NSLog(#"textView.frame.size.height: %f", textView.frame.size.height);
NSLog(#"textView.contentSize.height: %f", textView.contentSize.height);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
UPDATE3:
if ([[UIDevice currentDevice] majorVersionNumber] < 7.0) {
CGRect frame = _abstractTextView.frame;
UIEdgeInsets inset = _abstractTextView.contentInset;
frame.size.height = _abstractTextView.contentSize.height + inset.top + inset.bottom;
_abstractTextView.frame = frame;
}
else {
CGSize textViewSize = [_abstractTextView sizeThatFits:CGSizeMake(_abstractTextView.frame.size.width, FLT_MAX)];
_abstractTextView.frameHeight = textViewSize.height;
}
After you add the UITextView to its parent if you set a Content Mode on it then it seems to resize itself automatically.
This means you don't need to work out the height manually and apply a height contraint. It just seems to work!! Tested in iOS7 and iOS8 on iPad.
e.g.
--
textView.contentMode = UIViewContentMode.Center;
--
If anyone can explain why this works it would be much appreciated.. I found it by accident when messing with options in interface builder.
Just set scrollEnabled to NO, or uncheck Scrolling Enabled in the Scroll View section in IB and the UITextView will self-size.