I'm trying to resize a UITextView that I have in a UITableViewCell. I want to resize the UITableViewCell accordingly also. My UITableViewCell resizes properly, but the UITextView does not. It ends up being just two lines instead of sizing to the the correct size. When I initialize it in the cellForRowAtIndexPath method, it looks like:
UITextView *notesTextView = [[UITextView alloc] initWithFrame:CGRectMake(83, 12, TEXTVIEW_WIDTH, 22)];
notesTextView.tag = TEXTVIEW_TAG;
notesTextView.delegate = self;
[cell.contentView addSubview:notesTextView];
When I try to modify it in the textView delegate method, I do:
CGSize size = [textView.text sizeWithFont:textView.font constrainedToSize:CGSizeMake(TEXTVIEW_WIDTH, 460) lineBreakMode:UILineBreakModeWordWrap];
NSLog(#"size: %#", NSStringFromCGSize(size)); // has the correct size
if (size.height > self.notesRowHeight) {
self.notesRowHeight = size.height;
UITextView *aTextView = (UITextView *)[self.view viewWithTag:TEXTVIEW_TAG];
CGRect textViewFrame = CGRectMake(aTextView.frame.origin.x, aTextView.frame.origin.y, TEXTVIEW_WIDTH, size.height);
aTextView.frame = textViewFrame;
aTextView.contentSize = size;
[aTextView sizeToFit];
[aTextView sizeThatFits:size];
NSLog(#"%#", NSStringFromCGRect(aTextView.frame)); // has the correct height
So what ends up happening is the tableView resize properly, and even though the textView frame is the correct size (e.g. {{83, 12}, {197, 120}}), the textView only has 2 lines. It does not end up taking the size of the UITableViewCell like I intended.
EDIT: When I try to just resize the frame for the UITextView in a normal UIView that is not a UITableViewCell, it works just fine. So I'm not sure what's different in this case.
I found the answer. I should not reload the row, but just call
[tableView beginUpdates];
[tableView endUpdates];
More information can be found at:
UITextView in a UITableViewCell smooth auto-resize shows and hides keyboard on iPad, but works on iPhone
I found the best way to solve this.
First off, of course, you're going to want to create your UITextView and add it to your cell's contentView. I created an instance variable of UITextView called "cellTextView" Here is the code that I used:
- (UITableViewCell *)tableView:(UITableView *)tableView fileNameCellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
if (!cellTextView) {
cellTextView = [[UITextView alloc] initWithFrame:CGRectMake(5.0, 5.0, cell.bounds.size.width - 30.0, cell.bounds.size.height - 10.0)]; // I use these x and y values plus the height value for padding purposes.
}
[cellTextView setBackgroundColor:[UIColor clearColor]];
[cellTextView setScrollEnabled:FALSE];
[cellTextView setFont:[UIFont boldSystemFontOfSize:13.0]];
[cellTextView setDelegate:self];
[cellTextView setTextColor:[UIColor blackColor]];
[cellTextView setContentInset:UIEdgeInsetsZero];
[cell.contentView addSubview:cellTextView];
return cell;
}
Then, create an int variable called numberOfLines and set the variable to 1 in your init method. Afterwards, in your textViewDelegate's textViewDidChange method, use this code:
- (void)textViewDidChange:(UITextView *)textView
{
numberOfLines = (textView.contentSize.height / textView.font.lineHeight) - 1;
float height = 44.0;
height += (textView.font.lineHeight * (numberOfLines - 1));
CGRect textViewFrame = [textView frame];
textViewFrame.size.height = height - 10.0; //The 10 value is to retrieve the same height padding I inputed earlier when I initialized the UITextView
[textView setFrame:textViewFrame];
[self.tableView beginUpdates];
[self.tableView endUpdates];
[cellTextView setContentInset:UIEdgeInsetsZero];
}
Finally, paste this code into your heightForRowAtIndexPath method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
float height = 44.0;
if (cellTextView) {
height += (cellTextView.font.lineHeight * (numberOfLines - 1));
}
return height;
}
I will give you an different view....
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
.
.
if(_YOUR_ROW_ && _YOUR_SECTION_)
{
cell.textView.text = Text_Of_TextView;
}
.
.
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
.
.
if(_YOUR_ROW_ && _YOUR_SECTION_)
{
UIFont * FontSize = [UIFont systemFontOfSize:14.0];
CGSize textSize = [Text_Of_TextView sizeWithFont:FontSize constrainedToSize:CGSizeMake(Width_OF_textView, MAXFLOAT) lineBreakMode:UILineBreakModeWordWrap];
return textSize.height+PADDING;
}
.
.
}
-(void)textViewDidEndEditing:(UITextView *)textView
{
[Text_Of_TextView setText:textView.text];
CGRect frame = textViewInRow.frame;
frame.size.height = textViewInRow.contentSize.height;
textViewInRow.frame = frame;
[TableView_InView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:_YOUR_ROW_ inSection:_YOUR_SECTION_]] withRowAnimation:UITableViewRowAnimationNone];
}
and also dont forget to implement auto resizing behaviour for textview
This is a simple project that demonstrates how to use auto layout to resize the cells as of iOS 8. Almost no special code required. See readme.md for key points.
project on github
Related
I have a label whose text is longer that the table view's header containing it, so I want the label to be split into N lines according to the width available. This is my code:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if (section == 1) {
UIView *wrapper = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 100)];
[wrapper setBackgroundColor:[UIColor clearColor]];
UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 100)];
textLabel.text = NSLocalizedString(#"This is supposed to be a very long text that may fill several lines", #"");
[textLabel setLineBreakMode:UILineBreakModeWordWrap];
[textLabel setNumberOfLines:0];
[wrapper addSubview:textLabel];
return wrapper;
}
else
return nil;
}
However, the label is in a single line and I can't see the end of the text. What am I missing?
Thanks!
You should implement the delegate method heightForHeaderInSection.
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if(section ==1)
return 100;
else
return 0;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if (section == 1) {
NSString *text = NSLocalizedString(#"This is supposed to be a very long text that may fill several lines", #"");;
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:10] constrainedToSize:CGSizeMake(self.tableView.frame.size.width, 999) lineBreakMode:NSLineBreakByWordWrapping];
CGRect frame = CGRectZero;
frame.size = size;
UIView *wrapper = [[UIView alloc] initWithFrame:frame];
[wrapper setBackgroundColor:[UIColor clearColor]];
UILabel *textLabel = [[UILabel alloc] initWithFrame:frame];
textLabel.text = NSLocalizedString(#"This is supposed to be a very long text that may fill several lines", #"");
[textLabel setLineBreakMode:UILineBreakModeWordWrap];
[textLabel setNumberOfLines:0];
[wrapper addSubview:textLabel];
return wrapper;
}
else
return nil;
}
use this if you want to support iOS below 6.0
textLabel.lineBreakMode = UILineBreakModeWordWrap;
textLabel.numberOfLines = 0;
As UILineBreakModeWordWrap deprecated now.
otherwise use this
textLabel.lineBreakMode = NSLineBreakByWordWrapping;
textLabel.numberOfLines = 0;
you should set size of label as text in heightForRowAtIndexPath
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *text;
CGSize textViewSize;
int width = 270;
textViewSize = [text sizeWithFont:[UIFont fontWithName:#"Helvetica Neue" size:14.0]
constrainedToSize:CGSizeMake(width, FLT_MAX)
lineBreakMode:UILineBreakModeCharacterWrap];
CGFloat height = MAX(textViewSize.height, 44.0f);
return height;
}
and in cellForRowAtIndexPath write this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
textViewSize = [text sizeWithFont:[UIFont fontWithName:#"Helvetica Neue" size:14.0]
constrainedToSize:CGSizeMake(width, FLT_MAX)
lineBreakMode:UILineBreakModeCharacterWrap];
// / CGFloat height = MAX(textViewSize.height, 44.0f);
// if (!lblParameter){
// lblParameter = (UILabel*)[cell viewWithTag:1];}
CGFloat height = MAX(textViewSize.height, 44.0f);
[lblParameter setText:text];
[lblParameter setFont:[UIFont fontWithName:#"Helvetica Neue" size:14.0]];
[lblParameter setFrame:CGRectMake(10, 0,width,height)];
[[cell contentView] addSubview:lblParameter];
}
you need caculate the height of uilabel this height equal font size * numberline
It seems that wrapping / formatting will not work on a label inside the header view - presumably because it's inheriting defaults from somewhere. So, a simple solution, which AFAIK breaks no laws, is to put the label inside a view inside the header view.
Using the storyboard, drag a view to the table view controller (above your prototype cell).
Add a second view to this view.
Add the label to this second view. Make sure both views and label are tall enough to allow for multiple lines of text. In the label's attributes, set Lines to 0 and Line Breaks to Word Wrap.
Wrapping will now work as expected.
I'm trying to create a simple Diary app. In it I have a ShowPostTableViewController where I want to display each post. In my tableView I have one section with two cells, one for the headline and one for the post's body. The headline is inside an UITextField and the body is inside a UITextView. I declare both like this:
UITextField * postHeadline;
UITextView * postText;
And i synthesize them in my implementation file. I setup the tableView's cells in the willDisplayCell method. It looks like this:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath {
tableView.allowsSelection = NO;
CGRect wholeWindow = [self.view bounds];
float headlineHeight = 40;
CGRect headlineRect = CGRectMake(10, 10, wholeWindow.size.width, headlineHeight);
CGRect bodyRect = CGRectMake(wholeWindow.origin.y, wholeWindow.origin.x,
wholeWindow.size.width, wholeWindow.size.height);
postHeadline = [[UITextField alloc] initWithFrame:headlineRect];
postText = [[UITextView alloc] initWithFrame:bodyRect];
NSString * headline = [[NSString alloc] initWithFormat:#"%#",[currentPostArray
objectAtIndex:0]];
NSString * body = [[NSString alloc] initWithFormat:#"%#",[currentPostArray
objectAtIndex:1]];
postHeadline.text = headline;
postHeadline.font = [UIFont fontWithName:#"Helvetica-Bold" size:24.0];
postText.text = body;
postText.backgroundColor = [UIColor colorWithRed:254.0/255.0 green:255.0/255.0
blue:237.0/255.0 alpha:1];
postText.font = [UIFont fontWithName:#"Georgia" size:17.0];
postText.scrollEnabled = NO;
postText.delegate = self;
if (indexPath.row == 0) {
[cell.contentView addSubview:postHeadline];
}
if (indexPath.row == 1) {
[cell.contentView addSubview:postText];
CGRect frame = postText.frame;
frame.size.height = postText.contentSize.height;
postText.frame = frame;
}
}
Yep, I'm a beginner, probably leaking memory like crazy. A question related to that. It seems like my UITextField postHeadline gets set twice, I can't erase it because it's two layers of the same text. How can I solve that?
Back to my original question. My cellForRowAtIndexPath is left petty much intact. I have sat up the delegate methods for my UITextView (think it's here the problem lies). I found a solution that almost worked here: UITextView change height instead of scroll.
They look like this:
- (void)textViewDidBeginEditing:(UITextView *)textView
{
textView.frame =CGRectMake(textView.frame.origin.x,textView.frame.origin.y,textView.
frame.size.width,textView.frame.size.height + 100);
}
This method should resize the textView:
- (void)textViewDidChange:(UITextView *)textView
{
CGFloat fontHeight = (textView.font.ascender - textView.font.descender) + 1;
CGRect newTextFrame = textView.frame;
newTextFrame.size = textView.contentSize;
newTextFrame.size.height = newTextFrame.size.height + fontHeight;
textView.frame = newTextFrame;
}
I set the row height like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath
*)indexPath
{
NSString * body = [[NSString alloc] initWithFormat:#"%#",[currentPostArray
objectAtIndex:1]];
CGSize bodySize = [body sizeWithFont:[UIFont fontWithName:#"Georgia" size:17.0]
constrainedToSize:CGSizeMake(self.view.frame.size.width,CGFLOAT_MAX)];
if (indexPath.section == 0 && indexPath.row == 1) {
return bodySize.height + 100;
}
return 50;
}
The thing almost works. When the user hits return, the textview seems to expand, but it only works 14 times, then the cursor hides behind the keyboard.
Any ideas and tips how to solve this would be great!
Thanks
// Anders
EDIT
I solved the "headline gets set twice" problem by moving most of my willDisplayCell method code to my viewDidLoad method. And I think I have localized where the rezising problem is. I think it is in my heightForRowAtIndexPath method. It currently looks like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath
*)indexPath
{
NSString * body = [[NSString alloc] initWithFormat:#"%#",[currentPostArray
objectAtIndex:1]];
CGSize bodySize = [body sizeWithFont:[UIFont fontWithName:#"Georgia" size:17.0]
constrainedToSize:CGSizeMake(self.view.frame.size.width,CGFLOAT_MAX)];
if (indexPath.section == 0 && indexPath.row == 1) {
return bodySize.height + 100;
}
return 50;
}
Since array and it's content gets set in the viewDidLoad method, the size stays the same. I therefore think I need to dynamically increase the cell's size after my TextView's content too. Because when I write like this:
if (indexPath.section == 0 && indexPath.row == 1) {
return 1000;
}
The cell's size increases and the user can hit "return" more times before the keyboard gets in the the way. Is there a way to increase to cells's height dynamically?
Think I solved it. These methods did the magic trick:
[self.tableView beginUpdates];
[self.tableView endUpdates];
I updated my heightForRowAtIndexPath so it looks like this:
if (indexPath.section == 0 && indexPath.row == 1) {
return postText.frame.size.height + headlineHeight;
}
return 44;
And I added these methods to viewDidChange and in viewWillAppear (to reset the sizes).
[self.tableView beginUpdates];
[self.tableView endUpdates];
That was pretty much it.
In my UITableViewCells I am displaying text of varying lengths. To accomodate larger amounts of text and while also not requiring small texts amounts to be in huge table cells I am setting the height of the rows here...
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
float padding = 40.0f;
CGSize constraintSize;
constraintSize.width = 320.0f - padding - padding;
constraintSize.height = MAXFLOAT;
CGSize theSize = [thisrowstext sizeWithFont:[UIFont systemFontOfSize:14.0f]
constrainedToSize:constraintSize
lineBreakMode:UILineBreakModeWordWrap];
if(theSize.height < 24.0f) return 44.0f;
else return theSize.height + 20.0f;
}
...it works well, unfortunately the textLabel's width seems to be affected too with some textLAbels (depending on row count) being pushed in a few pixels. I've tried setting indentation values, but that doesn't work. Has anyone else encountered this?
EDIT: I'm adding the layoutSubviews method of my UITableViewCell subclass that i'm using (no NIB).
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.hideImage)
{
self.imageView.alpha = 0.0f;
self.imageView.frame = CGRectMake(-40.0f, 1.0f, 40.0f, 40.0f);
CGRect frame = self.textLabel.frame;
self.textLabel.frame = CGRectMake(frame.origin.x - 40.0f, frame.origin.y, frame.size.width + 40.0f, frame.size.height);
[self.textLabel setNeedsLayout];
}
else
{
self.imageView.alpha = 1.0f;
self.imageView.frame = CGRectMake(1.0f, 1.0f, 40.0f, 40.0f);
[self.textLabel setNeedsLayout];
}
}
EDIT: Also adding cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *TableCellViewWithHidableImageIdentifier = #"TableCellViewWithHidableImage";
TableCellViewWithHidableImage *cell = (TableCellViewWithHidableImage *)[tableView dequeueReusableCellWithIdentifier:TableCellViewWithHidableImageIdentifier];
if (cell == nil)
{
cell = [[[TableCellViewWithHidableImage alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TableCellViewWithHidableImageIdentifier] autorelease];
}
cell.hideImage = NO;
cell.imageView.image = [UIImage imageNamed:#"empty.png"];
cell.textLabel.font = [UIFont systemFontOfSize:16.0f];
cell.textLabel.numberOfLines = 0;
cell.textLabel.text = #"whatever";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
It's hard to tell without seeing how you generate your table cells. Do you use a nib for your cells? I've found it much easier to use a nib for custom table cells, so try that if you aren't already.
I suspect you may have a problem with your autoresizeMask on one or more of your table cells' subviews.
This is a bit of a guess, but it looks to me like it is the imageView on the left hand side of the cell. Seems like as the cell gets taller, it gets wider (trying to maintain aspect ratio?) and this is pushing your text to the right. What's odd is you image is not stretched. Might be worthwhile looking at what is happening to that image view during layoutSubviews. If your custom cell does not implement this method, maybe the base class' implementation is doing something you don't expect. You could override it and NSLog to the frame of the image view before and after the a call to [super layoutSubviews] to see whats going on.
I have a UITextView in a UITableViewCell contentview and allow the cell to autoresize so that the entered text is fully shown - what I am trying to accomplish is an autoresizing cell like the native iOS4 Contacts app has, when you enter "notes" for contact - i.e. when the contentSize of the textView changes - I call reloadRowsAtIndexPaths and in the delegate's heightForRowAtIndexPath I provide the new height for row - this does the job, however it is not nice and smooth like the contact's app - I am almost sure Apple uses some undocumented trick in that app to make the cell's contentView expand smooth and animated without calling reloadRowsAtIndexPaths. My question is how would you suggest to implement such functionality? I hope I didn't miss any details in explanation.
Try this code below, it will be help. You don't have to use any reload functions like reloadRowsAtIndexPaths.
// textview delegate
- (void)textViewDidChange:(UITextView *)textView {
if (contentView.contentSize.height > contentRowHeight) {
contentRowHeight = contentView.contentSize.height;
[theTableView beginUpdates];
[theTableView endUpdates];
[contentView setFrame:CGRectMake(0, 0, 300.0, contentView.contentSize.height)];
}
}
// tableview delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat height;
if (indexPath.row == 0)
height = kTitleRowHeight;
else
height = contentRowHeight;
return height;
}
I found the best way to solve this.
First off, of course, you're going to want to create your UITextView and add it to your cell's contentView. I created an instance variable of UITextView called "cellTextView" Here is the code that I used:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
if (!cellTextView) {
cellTextView = [[UITextView alloc] initWithFrame:CGRectMake(5.0, 5.0, cell.bounds.size.width - 30.0, cell.bounds.size.height - 10.0)]; // I use these x and y values plus the height value for padding purposes.
}
[cellTextView setBackgroundColor:[UIColor clearColor]];
[cellTextView setScrollEnabled:FALSE];
[cellTextView setFont:[UIFont boldSystemFontOfSize:13.0]];
[cellTextView setDelegate:self];
[cellTextView setTextColor:[UIColor blackColor]];
[cellTextView setContentInset:UIEdgeInsetsZero];
[cell.contentView addSubview:cellTextView];
return cell;
}
Then, create an int variable called numberOfLines and set the variable to 1 in your init method. Afterwards, in your textViewDelegate's textViewDidChange method, use this code:
- (void)textViewDidChange:(UITextView *)textView
{
numberOfLines = (textView.contentSize.height / textView.font.lineHeight) - 1;
float height = 44.0;
height += (textView.font.lineHeight * (numberOfLines - 1));
CGRect textViewFrame = [textView frame];
textViewFrame.size.height = height - 10.0; //The 10 value is to retrieve the same height padding I inputed earlier when I initialized the UITextView
[textView setFrame:textViewFrame];
[self.tableView beginUpdates];
[self.tableView endUpdates];
[cellTextView setContentInset:UIEdgeInsetsZero];
}
Finally, paste this code into your heightForRowAtIndexPath method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
float height = 44.0;
if (cellTextView) {
height += (cellTextView.font.lineHeight * (numberOfLines - 1));
}
return height;
}
I can't seem to get the text to actually span multiple lines. The heights look correct. What am I missing?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:#"StatusCell"] autorelease];
CGRect frame = cell.contentView.bounds;
UILabel *myLabel = [[UILabel alloc] initWithFrame:frame];
myLabel.text = [[person.updates objectAtIndex:indexPath.row] valueForKey:#"text"];
[cell.contentView addSubview:myLabel];
[myLabel release];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *text = [[person.updates objectAtIndex:indexPath.row] valueForKey:#"text"];
UIFont *font = [UIFont systemFontOfSize:[UIFont systemFontSize]];
CGSize withinSize = CGSizeMake(tableView.frame.size.width, 1000);
CGSize size = [text sizeWithFont:font constrainedToSize:withinSize lineBreakMode:UILineBreakModeWordWrap];
return size.height + 20;
}
Also, what am I missing that makes the labels appear longer than the table cell?
Tweetero provides an example of this in MessageListController.m. The code there renders the following screen:
(Pic is taken from Mashable).
The basic implementation outline:
When constructing a UITableViewCell, create and add a UILabel as a subview in the manner shown in tableviewCellWithReuseIdentifier:. Look for the creation of TEXT_TAG label.
when enriching the UITableViewCell with views, ensure that you format label properly, as is done in configureCell:forIndexPath, similarly look for the label tag TEXT_TAG.
Return the appropriate height for each cell, as is done in tableView:heightForRowAtIndexPath.
myLabel.numberOfLines = 2;
Check the docs for full info on how to use this property.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return theSizeYouWantYourCellToBe;
}