Table View Cell Height using Row Height not Auto Layout Height - ios

I have 3 custom Table View Cell's in a UITableView. I have the the height of one cell perfect, but the other two cells I just want them to use AutoLayout for their height.
This is what I have, does anyone have another idea for the else statement?:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
id model = self.Model[indexPath.row];
if ([model isKindOfClass:[D self]]) {
ListTableViewCell *cellOne = [tableView dequeueReusableCellWithIdentifier:#"1Cell" forIndexPath:indexPath];
if (!cellOne) {
cellOne = [[ListTableViewCell alloc] init];
FR *fD = (FR *)model;
NSString *title = [NSString stringWithFormat:#"%#", fD.title];
NSString *dateString = [self timeSincePublished:fD.pubDate];
NSString *description = [self removeHTMLTags:fD.description];
NSString *link = [NSString stringWithFormat:#"%#", fD.link];
cellOne.labelHeadline.text = title;
cellOne.labelDescription.text = description;
cellOne.labelPublished.text = dateString;
}
// Make sure the cell's frame is updated
[cellOne setNeedsLayout];
[cellOne layoutIfNeeded];
CGFloat heightOne = [cellOne.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return heightOne + 2;
} else {
// This is where I need help for other 2 custom cells to just use AutoLayout height
return tableView.rowHeight;
}
Right now, return tableView.rowHeight is returning the hardcoded height that is set in the storyboard, regardless of how much is actually in the cell.
It should ignore that height and just use AutoLayout.
Do you know a way to do that? Thanks! Will gladly post any extra info if needed-

With auto layout you don't use heightForRowAtIndexPath.
Instead you can use something like this:
self.tableView.estimatedRowHeight = 44.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;

Related

Xcode 6: Label in Table Cell won't expand / display longer text upon expansion / line up with cell

The Problem:
I've got a label in a table cell as follows:
EDIT: While the tableview does not use AutoLayout, the xib for the cell does. This is what it looks like:
Now, the "..." means the review (the text) is too long to be displayed. The user can then click on the cell, which would expand the cell and the label downwards, which would then show the rest of the text.
Here's the code for the heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
if (tableView == self.rateAndReviewView.ratingsTable) {
BOOL isSelected = [self.selectedIndexPaths containsObject:indexPath];
CGFloat maxHeight = MAXFLOAT;
CGFloat minHeight = 40.0f;
if (isSelected){
CGFloat constrainHeight = isSelected?maxHeight:minHeight;
CGFloat constrainWidth = tableView.frame.size.width - 20.0f;
//
SQLReview *review = [reviewsArray objectAtIndex:indexPath.row];
NSString *text = review.comment;
CGSize constrainSize = CGSizeMake(constrainWidth, constrainHeight);
CGSize labelSize = [text sizeWithFont:[UIFont systemFontOfSize:15.0f]
constrainedToSize:constrainSize
lineBreakMode:NSLineBreakByWordWrapping];
CGFloat labelHeight = labelSize.height;
return MAX(labelHeight+20, 100.0f);
} else {
minHeight = 100.0f;
return minHeight;
}
}
}
And this is the code for the cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.rateAndReviewView.ratingsTable) {
static NSString *CellIdentifier = #"Cell";
VenueReviewCell *cell = (VenueReviewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell==nil){
cell = [[[NSBundle mainBundle] loadNibNamed:#"ReviewCell" owner:nil options:nil] objectAtIndex:0];
}
[cell.contentView.layer setBorderWidth:0.5f];
cell.contentView.backgroundColor = [UIColor whiteColor];
SQLReview *review = [reviewsArray objectAtIndex:indexPath.row];
cell.usernameLabel.text = (review.user_name == nil) ? #"" : review.user_name;
cell.datetimeLabel.text = (review.datetime == nil) ? #"" : [review.datetime substringToIndex:10];
cell.commentLabel.text = (review.comment == nil) ? #"" : review.comment;
cell.commentLabel2.text = (review.comment == nil) ? #"" : review.comment;
float overallScore = (review.overall == nil) ? 0.0f : [review.overall floatValue];
NSArray *overallImageViewArray = #[cell.overallStarImageView1, cell.overallStarImageView2, cell.overallStarImageView3, cell.overallStarImageView4, cell.overallStarImageView5];
if([selectedIndexPaths containsObject:indexPath]) {
cell.commentLabel.hidden = YES;
cell.commentLabel2.hidden = NO;
CGSize commentLabelComputeSize = [cell.commentLabel2.text sizeWithFont:cell.commentLabel2.font constrainedToSize:CGSizeMake(cell.commentLabel2.frame.size.width, CGFLOAT_MAX) lineBreakMode:cell.commentLabel2.lineBreakMode];
CGFloat commentLabelHeightOffset = (commentLabelComputeSize.height - cell.commentLabel2.frame.size.height > 0) ? (commentLabelComputeSize.height - cell.commentLabel2.frame.size.height) : 0;
cell.commentLabel2.frame = CGRectMake(cell.commentLabel2.frame.origin.x, cell.commentLabel2.frame.origin.y, cell.commentLabel2.frame.size.width, cell.commentLabel2.frame.size.height + commentLabelHeightOffset);
cell.commentLabel2.autoresizingMask = UIViewAutoresizingFlexibleHeight;
cell.commentLabel2.numberOfLines = 0;
} else {
cell.commentLabel.hidden = NO;
cell.commentLabel2.hidden = YES;
}
[CommonUtilities displayStarRatingsWithScore:overallScore starImageViewArray:overallImageViewArray];
return cell;
}
return nil;
}
The isSelected part from heightForRowAtIndexPath does work, and the table cell does visibly expand on tap, but the text remains the same - at least, as tested on iOS 7+ devices.
EDIT: I also logged out the values of cell.commentLabel2.frame, by adding this:
NSLog(#"VenueTabViewController: cellForRow: contains indexpath %#, size: %#", indexPath, NSStringFromCGRect(cell.commentLabel2.frame));
in both the if and else of cellForRowAtIndexPath, and it shows the following:
On first tap:
VenueTabViewController: cellForRow: contains indexpath {length = 2, path = 0 - 1}, size: {{20, 31}, {285, 60.579999999999998}}
On second tap:
VenueTabViewController: cellForRow: contains indexpath {length = 2, path = 0 - 2}, size: {{20, 31}, {285, 21}}
So it does change in size.
What I tried:
I googled this, and found out that [string sizeWithFont:[UIFont systemFontOfSize:15.0f] constrainedToSize:constrainSize lineBreakMode:NSLineBreakByWordWrapping]; has been depreciated. So instead, I replaced the lines relating to it above with:
CGSize labelSize = [text sizeWithAttributes:#{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}];
CGFloat labelHeight = ceilf(labelSize.height);
Which not only failed to work, it also stopped the cell from expanding, which I thought might be a step backwards.
I also tried adding the following, because why not:
VenueReviewCell *cell = (VenueReviewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell==nil){
cell = [[[NSBundle mainBundle] loadNibNamed:#"ReviewCell" owner:nil options:nil] objectAtIndex:0];
}
[cell.commentLabel sizeToFit];
But it's still a nope.
Then, I simply replaced commentLabelHeightOffset with 100, just to test if anything will change:
cell.commentLabel2.frame = CGRectMake(cell.commentLabel2.frame.origin.x, cell.commentLabel2.frame.origin.y, cell.commentLabel2.frame.size.width, cell.commentLabel2.frame.size.height + commentLabelHeightOffset);
Nothing happened.
EDIT: Here's what I did that finally gained traction - I removed the AutoLayout on the cells, as per the suggestion of saif. And now, while the cells do expand, and the labels along with it, for some reason I get this:
I checked the code, and the X and Y of the label frame aren't changed (just the frame height) and the text has no "new lines" before the actual text.
Can I have some help please?
Try this, Set the frame to desc label in cell for row(depending on text size), and reload the table view when you want to expand the cell.
In cellForRowAtIndexPath
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
//code to add desc label
}
CGSize suggestedSize = [descriptionText sizeWithFont:descLabel.font constrainedToSize:CGSizeMake(descLabel.frame.size.width, FLT_MAX) lineBreakMode:descLabel.lineBreakMode];
float padding = 20;
[descLabel setFrame:CGRectMake(padding, CGRectGetMaxY(nameLabel.frame), CGRectGetWidth(tableView.frame)-(2*padding), suggestedSize.height)];
if you are using auto layouts in custom cell then
Keep a back up, and uncheck auto layouts
select iPhone in drop down and select Disable size class
If there is a problem with row height,
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGSize suggestedSize = [descriptionText sizeWithFont:descLabel.font constrainedToSize:CGSizeMake(descLabel.frame.size.width, FLT_MAX) lineBreakMode:descLabel.lineBreakMode];
float heightOfRow = CGRectGetMaxY(nameLabel.frame)+suggestedSize.height+5; // height of name label(as it is single line supported) + height of desc label + bottom padding
return heightOfRow;
}
Hope this helps
Try autoresizing:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
VenueReviewCell *cell = (VenueReviewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.commentLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight;
cell.commentLabel.numberOfLines = 0;
return cell;
}
If you want to continue using autolayout:
-set the numberOfLines to '0'
-Implement the following two TableView methods
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}
Working with Self-Sizing Table View Cells Here is a useful reference.
If you're using manual layout then you must set the numberOfLines property of your label to 0, also you will want to call -sizeToFit on the label after setting the text.
You might also need to change some other frames after this accordingly so the label doesn't overlap anything.
If you are using AutoLayout then setting the numberOfLines is still needed but then it depends on your constraints, if you've set the left and right of the label to the super view's left and right then it can infer a maximum width and grow the label vertically.
In any case your screenshot clearly shows that the label is set to have numberOfLines = 1, your cell height looks okay, which is expected since you're calculating that in code with a set width. It's the rendering of the label which is not being triggered. Change numberOfLines to 0 to start with, then in layoutSubviews (if you're doing things manually) you'll want to sizeToFit

Table View height changes on tap or when popped back to

I have a table view that loads 3 custom cells. 1 custom cell has an ImageView in it, and it seems to be the culprit of the table view moving when any cell is tapped.
This is what I've tried, but it hasn't worked.
If I use a static value (i.e. return 180) it works just fine, but anything other than that the table view jumps.
Anyone know why? Thanks!
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
id model = self.Model[indexPath.row];
if ([model isKindOfClass:[FR self]]) {
ListTableViewCell *cellOne = [tableView dequeueReusableCellWithIdentifier:#"1Cell" forIndexPath:indexPath];
if (!cellOne) {
cellOne = [[ListTableViewCell alloc] init];
FR *fD = (FR *)model;
NSString *title = [NSString stringWithFormat:#"%#", fD.title];
NSString *dateString = [self timeSincePublished:fD.pubDate];
NSString *description = [self removeHTMLTags:fD.description];
NSString *link = [NSString stringWithFormat:#"%#", fD.link];
cellOne.labelHeadline.text = title;
cellOne.labelDescription.text = description;
cellOne.labelPublished.text = dateString;
}
// Make sure the cell's frame is updated
[cellOne setNeedsLayout];
[cellOne layoutIfNeeded];
CGFloat heightOne = [cellOne.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return heightOne + 2;
} else if ([model isKindOfClass:[D self]]) {
// more code
}
As far as I know, this is a bug. The workaround to avoid the table jumping right after tapping a cell is to add:
[self.tableview reloadData];
in your table viewWillDisappear
https://devforums.apple.com/message/1050163#1050163
https://devforums.apple.com/message/1042398#1042398

Multi-line UILabel

I've looked over older questions and tried all the suggestions, but still cannot seem to get a multi-line UILabel to work. I have a UITableView and the cell is created by tableView:cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *fieldValue = [self fieldValueAtIndexPath:indexPath];
NSString *fieldName = [self fieldNameAtIndexPath:indexPath];
NSString *title = [[self appDelegate] displayNameForFieldName:fieldName];
Field fieldCode = [[self appDelegate] fieldCodeForFieldName:fieldName];
DetailCell *cell = nil;
NSString *identifier = nil;
BOOL isNotes = [fieldName caseInsensitiveCompare:#"Notes"] == NSOrderedSame;
switch( isNotes ) {
case NO:
{
identifier = #"DetailCell";
cell = (DetailCell*)[tableView dequeueReusableCellWithIdentifier:identifier];
NSInteger rows = [self heightForText:fieldValue andFont:[self textFont] andWidth:cell.value.frame.size.width] / _oneRowSize.height;
cell.value.text = fieldValue;
cell.name.text = [title lowercaseString];
cell.name.numberOfLines = MAX( 1, rows );
cell.value.numberOfLines = cell.name.numberOfLines;
break;
}
case YES:
{
cell = (DetailCell *)[tableView dequeueReusableCellWithIdentifier:#"DetailCellNotes" forIndexPath:indexPath];
// cell = (DetailCell *)[tableView dequeueReusableCellWithIdentifier:#"DetailCellNotes"];
cell.value.text = #"This is a very long line of text which should take up several lines";
cell.name.text = [title lowercaseString];
cell.value.numberOfLines = 5; // No more than 5 lines of text
cell.value.backgroundColor = [UIColor purpleColor];
cell.value.lineBreakMode = NSLineBreakByWordWrapping;
cell.value.frame = CGRectMake(cell.value.frame.origin.x, cell.value.frame.origin.y, 180, 70);
[cell.value sizeThatFits:CGSizeMake(180., 70.)];
break;
}
}
cell.fieldName = fieldName;
return cell;
}
The height in the table view is defined like so
- (CGFloat) tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath
{
NSString *fieldName = [self fieldNameAtIndexPath:indexPath];
CGFloat height = 0.0;
if([fieldName isEqualToString:#"Notes"])
{
height = 70.;
}
else if([fieldName isEqualToString:#"Image"])
{
height = 100.;
};
return height;
}
which makes the cell large enough to hold a 3-line label. However when the cell appears the label is only one line (shown by the background being purple).
The tableview uses prototype cells, and I've also tried to set it to numberOfLines=5 and WordWrapping, but that didn't change the effects either. I've also tried both of the commented out lines (though searches suggest that sizeToFit might actually reset numberOfLines to 1).
I wonder what I've missed. I can't see any other place where the it might be overridden.
Thanks.
You are calling dequeueReusableCellWithIdentifier: to create your cell. This is a mistake, because it means that the cell has not assumed its final size. It is much better to call dequeueReusableCellWithIdentifier:forIndexPath:. This means that the cell will actually have the height that you are giving it in heightForRowAtIndexPath:. You should then be able to set the height of the label successfully.

UITableView cell contents not displayed properly

I use auto layout and want to display UITableView with custom cell that has a UITextView with variable content.
Cells are displayed as shown below. For chat message, when cell is first displayed only 2-3 characters are displayed in a single line and next characters are forced to go to next line. But when I scroll tableView so that these cells are not visible and scroll back to make them visible (I do this so that when they are visible again cellForRowAtIndexPath: method is called again to render the cell), they render the cell properly as shown in second image.
Code :
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [chatData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RS_User *user = [[RS_User alloc]init];
chatCell *cell = (chatCell *)[tableView dequeueReusableCellWithIdentifier:CHAT_CELL_IDENTIFIER];
NSUInteger row = indexPath.row;
cell.chatCellBackground.image = nil;
NSString *chatText = [[chatData objectAtIndex:row] objectForKey:TEXT]; // get text string(message) from array
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
UIFont *font = [UIFont systemFontOfSize:FONT_SIZE];
cell.textString.font = [UIFont fontWithName:FONT_NAME size:FONT_SIZE]; // set text font
cell.textString.text = chatText;
// set text
CGSize size = [cell.textString.text sizeWithFont:cell.textString.font constrainedToSize:CGSizeMake(SET_WIDTH, MAXFLOAT) lineBreakMode:NSLineBreakByCharWrapping];
cell.textString.frame = ...;
[cell.textString sizeToFit];
NSDate *theDate = [[chatData objectAtIndex:row] objectForKey:DATE];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:DATE_FORMAT];
NSString *timeString = [formatter stringFromDate:theDate];
cell.timeLabel.text = timeString; // set timeLabel to display date and time
cell.timeLabel.font = [UIFont fontWithName:FONT_NAME size:SMALL_FONT_SIZE];
cell.userLabel.text = #"Name";//[[chatData objectAtIndex:row] objectForKey:NAME]; // set userLabel to display userName
CGSize size1 = [cell.userLabel.text sizeWithFont:font constrainedToSize:CGSizeMake(SET_WIDTH, MAXFLOAT) lineBreakMode:NSLineBreakByCharWrapping];
// check cell contains sender name or receiver name
if (thisCellIsForSender)
{
// Set bubble for sender
cell.chatCellBackground.image = [[UIImage imageNamed:#"bubbleMine.png"] stretchableImageWithLeftCapWidth:STRETCHED_WIDTH topCapHeight:STRETCHED_HEIGHT];
cell.chatCellBackground.frame = ...;
}
else
{
// set bubble for receiver
cell.chatCellBackground.image = [[UIImage imageNamed:#"bubbleSomeone.png"] stretchableImageWithLeftCapWidth:STRETCHED_WIDTH topCapHeight:STRETCHED_HEIGHT];
cell.chatCellBackground.frame = ...;
}
[cell setNeedsLayout];
[cell layoutIfNeeded];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
RS_User *user = [[RS_User alloc]init];
chatCell *cell = (chatCell *)[tableView dequeueReusableCellWithIdentifier:CHAT_CELL_IDENTIFIER];
NSUInteger row = indexPath.row;
cell.chatCellBackground.image = nil;
NSString *chatText = [[chatData objectAtIndex:row] objectForKey:TEXT]; // get text string(message) from array
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
UIFont *font = [UIFont systemFontOfSize:FONT_SIZE];
cell.textString.font = [UIFont fontWithName:FONT_NAME size:FONT_SIZE]; // set text font
cell.textString.text = chatText;
// set text
CGSize size = [cell.textString.text sizeWithFont:cell.textString.font constrainedToSize:CGSizeMake(SET_WIDTH, MAXFLOAT) lineBreakMode:NSLineBreakByCharWrapping];
cell.textString.frame = ...;
[cell.textString sizeToFit];
NSDate *theDate = [[chatData objectAtIndex:row] objectForKey:DATE];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:DATE_FORMAT];
NSString *timeString = [formatter stringFromDate:theDate];
cell.timeLabel.text = timeString; // set timeLabel to display date and time
cell.timeLabel.font = [UIFont fontWithName:FONT_NAME size:SMALL_FONT_SIZE];
cell.userLabel.text = [[chatData objectAtIndex:row] objectForKey:NAME]; // set userLabel to display userName
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
CGSize size1 = [cell.userLabel.text sizeWithFont:font constrainedToSize:CGSizeMake(SET_WIDTH, MAXFLOAT) lineBreakMode:NSLineBreakByCharWrapping];
// check cell contains sender name or receiver name
if (thisCellIsForSender)
{
// Set bubble for sender
cell.chatCellBackground.image = [[UIImage imageNamed:#"bubbleMine.png"] stretchableImageWithLeftCapWidth:STRETCHED_WIDTH topCapHeight:STRETCHED_HEIGHT];
cell.chatCellBackground.frame = ...;
}
else
{
// set bubble for receiver
cell.chatCellBackground.image = [[UIImage imageNamed:#"bubbleSomeone.png"] stretchableImageWithLeftCapWidth:STRETCHED_WIDTH topCapHeight:STRETCHED_HEIGHT];
cell.chatCellBackground.frame = ...;
}
cell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));
[cell setNeedsLayout];
[cell layoutIfNeeded];
return cell.bounds.size.height;
}
UPDATE : I could solve some of the issues from coverback's answer. Still cell is not laid out correctly. I am adding photos for constraints of each UI object in cell.
User name :
Time stamp :
Chat message :
Bubble image :
Cell Layout :
Cell height in cellForRowAtIndexPath: is always 100 even though I return different value from heightForRowAtIndexPath: method. Cell height in storyboard is 100.
Chat message in third cell is clipped from bottom.
Time stamps and chat message labels are some time not aligned properly to each other even though both have constraint vertical spacing from username label.
Username label in third cell is vanished.
Do you see anything wrong in constraints? You can ask me if analyzing constraints is difficult.
Combining the answers by thandasoru and coverback solved my issue. Below is the code I use.
I added below method in custom UITableViewCell's class.
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat maxTextWidth = [UIScreen mainScreen].bounds.size.width * 0.40;
self.userLabel.preferredMaxLayoutWidth = maxTextWidth;
self.chatMessage.preferredMaxLayoutWidth = maxTextWidth;
self.timeLabel.preferredMaxLayoutWidth = self.timeLabel.frame.size.width;
[super layoutSubviews];
}
UITableView datasource methods :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell * cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:#"customCellId"];
// Set all label's text and image to ImageView
...
// Update layout
[cell setNeedsLayout];
[cell layoutIfNeeded];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
customCell * cell = (customCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
NSString * userName = cell.userLabel.text;
NSString * chatText = cell.chatMessage.text;
// Get bounding rect of userName label
CGRect userNameFrame = [userName boundingRectWithSize:CGSizeMake(maxLabelWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:cell.userLabel.font} context:nil];
// Get bounding rect of chatMessage label
CGRect chatTextFrame = [chatText boundingRectWithSize:CGSizeMake(maxLabelWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:cell.chatMessage.font} context:nil];
// Calculate cell height from its contents
height = userNameFrame.size.height + chatTextFrame.size.height + PADDING_ABOVE_TOP_LABEL + DISTANCE_BETWEEN_TWO_LABELS + PADDING_BELOW_BOTTOM_LABEL;
return height;
You can determine the cell height dynamically so that the text appears on one line. I've given CGSize(320,568) as bounds. But you can change the size as you see fit
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *chatText = [[chatData objectAtIndex:row] objectForKey:TEXT];
CGRect frame = [chatText boundingRectWithSize:CGSizeMake(320,568) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:[UIFont systemFontOfSize:18.0f]} context:nil];
return frame.height; //returns cell's height after calculating the area that text needs
}
You are setting the frame of the text and calling sizeToFit:
CGSize size = [cell.textString.text sizeWithFont:cell.textString.font constrainedToSize:CGSizeMake(SET_WIDTH, MAXFLOAT) lineBreakMode:NSLineBreakByCharWrapping];
cell.textString.frame = ...;
[cell.textString sizeToFit];
This will not be working with autolayout. You need to remove setting the frame and sizeToFit completely, same goes for all other frame being set directly. If things don't work after that, it would mean some constraints are not correct. But mixing approaches always breaks layout.
For UILabels, make sure to also set preferredMaxLayoutWidth to label's width, otherwise text may not wrap correctly:
- (void)layoutSubviews
{
[super layoutSubviews];
self.label.preferredMaxLayoutWidth = self.label.frame.size.width;
[super layoutSubviews];
}
Also, your height calculating method should not just do same thing as cellForRowAtIndexPath:, if you want to use the same code, just call that other method to get a cell.

Cannot change frame of a UILabel in a UITableViewCell when first appearing (using Autolayout)

I have a prototype cell inside my UITableView which contains a UILabel. I want to dynamically change the size of the label (and the cell) depending on the size of the text in the label.
I create the prototype cell in cellForRowAtIndexPath like this:
static NSString *CellIdentifier = #"ProgramDetailCell";
ProgramDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.descriptionLabel.text = self.program.subtitle;
return cell;
Then my ProgramDetailCell has the following implementation:
#implementation ProgramDetailCell
- (void)layoutSubviews {
[super layoutSubviews];
[self.descriptionLabel sizeToFit];
}
#end
The first time the cell is displayed, layoutSubviews is called, but the descriptionLabel does not get resized. However, if I scroll down the table and back up again, the "reused" cell appears with the label correctly resized!
Why does it not work the first time the cell is displayed - and what do I need to do to fix it.
Thanks in advance!
In xib, go to first tab, under Interface Builder Document , uncheck use Auto Layout box.
It doesn't work because you are using auto layout. You need some auto layout way to achieve this.
Here's the solution.
Step 1 :
Have the UILabel Pinned to Top and Bottom of the UITableViewCell. You can achieve this through the Interface Builder by having Vertical Constraint on UILabel from top and bottom of the cell. This will make the UILabel increase in height if the cell's height increase or vice versa.
Step 2:
In your UIViewController in - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath method calculate the size of the UITableViewCell.
You can calculate it using :
CGRect textFrame = [YourText boundingRectWithSize:CGSizeMake(width of the label, FLT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:Your Font} context:nil];
now return the textFrame.size.height + padding if present.
Step 3 :
You will achieve what you wanted --- "dynamically change the size of the label (and the cell) depending on the size of the text in the label" after compiling and running even for the first time.
After retrieving all data from server you can get height of that UILabel using this method.
CGSize maximumSize = CGSizeMake(210, 9999);
for (int i = 0; i < [_arrProductList count]; i++) {
float row_height = 0.0f;
ProductInformation *product_obj = [_arrProductList objectAtIndex:i];
CGSize desc_size = [self measureHeightForText:product_obj.product_desc forFont: [UIFont systemFontOfSize:14] forSize:maximumSize];
row_height = row_height + desc_size.height;
// [_arrRowHeights addObject:[NSString stringWithFormat:#"%f", row_height]]; You can take it into array.
}
[tableView reloadData];
And here i have given description of measureHeightForText:. This logic is working in all iOS5,iOS6,iOS7.
-(CGSize)measureHeightForText:(NSString *)strText forFont:(UIFont *)font forSize:(CGSize)size{
if (!testingLabel) {
testingLabel = [[UILabel alloc] init];
// testingLabel.font = [UIFont fontWithName:[AppHandlers zHandler].fontName size:16];
testingLabel.text = #"";
testingLabel.numberOfLines = 0;
}
testingLabel.text =strText;
testingLabel.font = font;
CGSize expectedSize = [testingLabel sizeThatFits:size];
return expectedSize;
}
And then update size of your label according it. This is working fine for me. I am using it.
Because when layoutSubviews is called your descriptionLabel's text is not set yet. And when you scroll, the text is set. So it is correct now.
I suggest you call sizeToFit after you set the text.
static NSString *CellIdentifier = #"ProgramDetailCell";
ProgramDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.descriptionLabel.text = self.program.subtitle;
[cell.descriptionLabel sizeToFit];
return cell;
Call this in heightForRowAtIndexPath and manually calculate the height
static NSString *CellIdentifier = #"ProgramDetailCell";
ProgramDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.descriptionLabel.text = self.program.subtitle;
[cell.descriptionLabel sizeToFit];
return cell.descriptionLabel.frame.size.height+cell.descriptionLabel.frame.origin.y;
You can use below code:
//Calculate the expected size based on the font and linebreak mode of your label
// FLT_MAX here simply means no constraint in height
CGSize maximumLabelSize = CGSizeMake(296, FLT_MAX);
CGSize expectedLabelSize = [yourString sizeWithFont:yourLabel.font constrainedToSize:maximumLabelSize lineBreakMode:yourLabel.lineBreakMode];
//adjust the label the the new height.
CGRect newFrame = yourLabel.frame;
newFrame.size.height = expectedLabelSize.height;
yourLabel.frame = newFrame;
If your requirement is dynamically changing label height mean follow the below link. When we need to change height of label we need to change the row height also:
Resizing UILabel not quite working
Use below code for dynamic height set in cell
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *messagetext=[NSString stringWithFormat:#"%#",[[arryStoryView objectAtIndex:indexPath.row] valueForKey:#"messagetext"]];
CGSize StoryTextSize= [messagetext sizeWithFont:[UIFont fontWithName:#"Georgia" size:17.0f] constrainedToSize:CGSizeMake(300, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];
int TotalHeight;
TotalHeight= StoryTextSize.height + 10;
return TotalHeight;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell1";
UITableViewCell *cell=[tblSendMsg dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
NSString *storytext=[NSString stringWithFormat:#"%#",[[arryStoryView objectAtIndex:indexPath.row] valueForKey:#"storytext"]];
CGSize StoryTextSize = [storytext sizeWithFont:[UIFont fontWithName:#"Georgia" size:17.0f] constrainedToSize:CGSizeMake(300, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];
lblStoryText.frame=CGRectMake(5, 25, [[UIScreen mainScreen] bounds].size.width-10, StoryTextSize.height+30);
int nooflines=StoryTextSize.height/16;
int i= StoryTextSize.height;
if(i%16 !=0)
{
nooflines=nooflines+1;
}
lblStoryText.numberOfLines=nooflines;
lblStoryText.font=[UIFont fontWithName:#"Georgia" size:17.0f];
lblStoryText.text=[NSString stringWithFormat:#"%#",storytext];
return cell;
}
If you are using autolayout, modifying the frame will have no effect.
Instead, you should modify the constraints.

Resources