Wrong UITableViewCell height with UITableViewAutomaticDimension - ios

I make custom cell with 2 multiline labels and pin this labels to all sides. When in -tableView:heightForRowAtIndexPath: for iOS >= 8 I return UITableViewAutomaticDimension. But when table view appears the cell height is bigger than should be. After I scroll down and then scroll up the cell takes the normal height. How to fix this behavior?
Code for height:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {
return UITableViewAutomaticDimension;
}
static CCTopicTableViewCell *cell;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cell = [tableView dequeueReusableCellWithIdentifier:#"topicCellIdentifier"];
});
cell.topic = [self.topics topicAtIndexPath:indexPath];
cell.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(cell.bounds));
[cell setNeedsLayout];
[cell layoutIfNeeded];
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height + 1;
}
And the result:
After scrolling:

The problem is gone when I don't set tableView's estimatedRowHeight for iOS 8 and update preferredMaxLayoutWidth every time when sets the text for multiline labels

I solved this issue by setting "text" property of multiline labels to "nil":
override func prepareForReuse() {
_label.text = nil
}
Also table properies "estimatedRowHeight" and "rowHeight" should be set correctly:
_tableView.estimatedRowHeight = 44
_tableView.rowHeight = UITableViewAutomaticDimension

Automatic dimension is not working well for some UILabel if that label is aligned with different label by top edges or bottom edges. Just make sure that you do not have such alignments.

Set automatic dimension in viewDidLoad() method or viewWillAppear() (both code in same place)
tableView.rowHeight = UITableViewAutomaticDimension;
tableView.estimatedRowHeight = 160.0;//(Maximum Height that should be assigned to your cell)
Visit Using Auto Layout in UITableView for dynamic cell layouts & variable row heights an intuitive guide for both iOS 7 and iOS 8

Related

Changing UILabel frame height inside tableView cell

I get my UILabel text from remote url, and sometimes the text is too short/too long and I need to adapt my frame size to the text height.
I have implemented costume cell using xib.
This is what I tried so far, which supposed to works, but don't work for a reason I can't understand:
cell.fbLastObject.text = [NSString stringWithFormat:#"%#",update.fbLastObject];
[cell.fbLastObject sizeToFit];
//Cell Size
CGSize labelSize = [cell.fbLastObject.text sizeWithFont:cell.fbLastObject.font
constrainedToSize:cell.fbLastObject.frame.size
lineBreakMode:NSLineBreakByWordWrapping];
labelHeight = labelSize.height;
cell.fbLastObject.frame = CGRectMake(0,0,300,labelHeight);
Anyone has any idea?
For iOS 8 and later, you can use UITableViewAutomaticDimension Put the below code in the viewDidLoad is:
tableView.rowHeight = UITableViewAutomaticDimension;
/* any estimated height but must be more than 2 */
tableView.estimatedRowHeight = 160.0;
tableview.delegate = self;
tableview.dataSource = self;
// you have created the SimpleTableViewCell.xib, So you need to register that cell with table.
UINib *nib = [UINib nibWithNibName:#"SimpleTableViewCell" bundle:nil];
[tableview registerNib:nib forCellReuseIdentifier:#"SimpleTableViewCell"];
Now add proper cell fbLastObject constraints (top, bottom, leading and trailing) pins to super View & set the number of lines to 0
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"SimpleTableViewCell";
SimpleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// here set text for your cell label
cell.fbLastObject.text = #"My Text";
[cell setNeedsLayout];
[cell layoutIfNeeded];
return cell;
}
You can try self.tblView.rowHeight = UITableViewAutomaticDimension;
For dynamic cell using autolayout
You can do by mapping Height Constraint of UILabel and calculate the size of the text from remote url and according to that calculated height return height of row and also give that height to mapped Height Constraint outlet and before return cell just write :-
[cell.UILabel updateConstraints];
In your question the steps you took to get the size for the Label is fine. Make Label's property SizeToFit and NumberOfLines to 0 and add the appropriate constraints to it. Then create an instance of NSLayoutConstraint for Height constraint of Label and make it attach to bottom of UITableViewCell. Then when ever you have the CGSize from text then you need to call UpdateConstraintIfNeeded on self and then need to set height constant property for that Label's constraint & need to set the heightForRowAtIndexpath of UITableVIew.
Hope this will work.

UITextView sizeThatFits returns different size than boundingRectWithSize

Need the required height of UITextView. sizeThatFits returns bigger, but the correct height than boundingRectWithSize. Why difference exist?
At two places I need to know the height. In cellForRowAtIndexPath and in heightForRowAtIndexPath.
I do not think it is efficient to create always a UITextView in heightForRowAtIndexPath just to know what height is required.
What workaround do you know to calculate height of a UITextView in heightForRowAtIndexPath?
I met similar problem last month for UITableView, and I use boundingRectWithSize to calculate the size, it is actually correct. I then put it into UITextView.
Some mistakes I made:
I forget to set the same font size when calculating and for UITextView
UITextView has margins, I will manually add it in heightForRowAtIndexPath and set textContainerInset to the same one.
Hope it helps you.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger section = indexPath.section;
NSUInteger axisIndex = section - 2;
yAxis *yAxisObj = self.yAxisInfoArray[axisIndex];
boundingRect = [yAxisObj.yAxisDescription boundingRectWithSize:CGSizeMake(self.descriptionViewWidth, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading
attributes:#{NSFontAttributeName:self.contentFont}
context:nil];
return boundingRect.size.height + TEXT_TOP_MARGIN + TEXT_BOTTOM_MARGIN;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellId = #"ChartDescriptionCell";
ChartDescriptionCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[ChartDescriptionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.textView.bounces = NO;
cell.textView.showsHorizontalScrollIndicator = NO;
cell.textView.showsVerticalScrollIndicator = NO;
cell.textView.font = self.contentFont;
cell.textView.textColor = [UIColor colorWithHex:#"#333333"];
cell.textView.textContainerInset = UIEdgeInsetsMake(TEXT_TOP_MARGIN, -5, TEXT_BOTTOM_MARGIN, -5);
}
NSInteger section = indexPath.section;
NSUInteger axisIndex = section - 2;
yAxis *yAxisObj = self.yAxisInfoArray[axisIndex];
cell.textView.text = yAxisObj.yAxisDescription;
}
return cell;
}
boundingRectWithSize returns size for text, so you should manually provide your font.
sizeThatFits returns size of UITextView with this text inside
If you are pointing to iOS 8 and above you can use Dynamic cell height which is very easy. In case of iOS 7 you need some workaround.
Tutorial: http://www.raywenderlich.com/87975/dynamic-table-view-cell-height-ios-8-swift
Related question with nice answer: Using Auto Layout in UITableView for dynamic cell layouts & variable row heights

Unexpected behavior of systemLayoutSizeFittingSize: and size classes

I've got some strange problem related to dynamically sized cells, auto layout and size classes. My test project is completely based on Ray's tutorial. It seems that the only difference is UILabels's font sizes for size classes.
The height calculation is wrong when I set another font size for label in some size class. I've made screenshot to illustrate it.
Wwith wrong cell's height calculations:
With correct cell's height calculations:
In addition, I've pushed test project to the github.
EDITED:
ViewController.m
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self heightForCellAtIndexPath:indexPath];
}
- (CGFloat)heightForCellAtIndexPath:(NSIndexPath *)indexPath {
static CustomTableViewCell *sizingCell = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sizingCell = [self.tableView dequeueReusableCellWithIdentifier:kBasicCell];
});
[self configurateCell:sizingCell atIndexPath:indexPath];
return [self calculateHeightForCell:sizingCell];
}
- (CGFloat)calculateHeightForCell:(CustomTableViewCell *)cell {
cell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.tableView.frame), CGRectGetHeight(cell.bounds));
[cell setNeedsLayout];
[cell layoutIfNeeded];
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height +1.0f;
}
CustomLabel.m
- (void)setBounds:(CGRect)bounds {
[super setBounds:bounds];
if (self.numberOfLines == 0 && bounds.size.width != self.preferredMaxLayoutWidth) {
self.preferredMaxLayoutWidth = self.bounds.size.width;
[self setNeedsUpdateConstraints];
}
Since you're already using Auto Layout, I'd recommend also taking advantage of self-sizing cells. You won't need any row height calculation, as iOS can adjust the cell's height automatically, based on the UILabel height.
Add Auto Layout constraints to your contentView's UILabel. This will cause the cell to adjust its contentView height based on the label's contents.
Enable row height estimation.
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0;
For more information, see the detailed walkthrough by smileyborg in his answer to Using Auto Layout in UITableView for dynamic cell layouts & variable row heights.

Scale a multi line label in UITableViewCell

I have a UITableView with multiple height for each cell:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == 0){
return 100;
}
else if (indexPath.row==7){
return 200;
}
else {
return 40;
}
}
The problem is in the 7th row with height 200, I have this:
It's just showing the end of the label.
I modified the number of lines of that label to "0" in IB.
What could be the problem.
Thank you for your help.
Increase the label's dimensions. Enlarge the label's size to occupy all the space available in the cell.
If you're using Xib for creating this cell. Attach struts to bottom of the view and let spring's enlarge it. If you're doing it programmatically, enlarge the frame like this:
label.frame = CGRectMake(50, 10, 320, 150); // replace these with appropriate values.
give
myLabel.numberOfLines = 0;
[myLabel sizeToFit];
Also this Question 1 and Question 2 is very helpful for you.

UITableView with UILabel SizeToFit get mess when Scrolling

Hi everyone I have problem with my tableview, i make Cell with uilabel with SizeToFit and then calculate the UILabel Height for set the cell Height everything work well except when i scrolling my tableView the text get weird like one char per line:
My TableViewVell Method is:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
UILabel *label = (UILabel *)[cell viewWithTag:1];
label.text = [someArray objectAtIndex:indexPath.row];
// Set the UILabel to fit my text;
messageLabel.lineBreakMode = UILineBreakModeWordWrap;
messageLabel.numberOfLines = 0;
[messageLabel sizeToFit];
return cell;
}
The Cell Height stay in the correct Size the problem only with the UILabel ...
this the result when i load the view:
First screenshot
And this is after i start scroll the TableView:
Second screenshot
Any Help???
The method sizeToFit respects the width of the frame and adjust the height. And table view cell reuse the cells. Combining those two behaviors can cause the issue you described.
One of your label, which is very short in content (e.g. in the 4th cell with the word "jio"), is resized with sizeToFit and the resulting width is smaller than the original width you intended.
After that, table view reuse the cell and sizeToFit still respects the small width calculated from previous call of sizeToFit.
Solutions:
1) set the width of the frame to your original intended label width everytime before you call sizeToFit:
CGRect frame = messageLabel.frame;
frame.size.width = 100.0; //you need to adjust this value
messageLabel.frame = frame;
2) use your row height calculation and set the frame height instead. No need to use sizeToFit.
you should probably subclass your cell and clean the UILabel on
override func prepareForReuse() {
super.prepareForReuse()
self.label.text = nil
}
This might help
http://www.cimgf.com/2009/09/23/uitableviewcell-dynamic-height/
The approach in given reference and solution provided by #Verbumdei is nearby same.
If you are not using autolayout, this can also be solved by adding a category on UIView
public func sizeToFit(constrainedSize: CGSize) -> CGSize {
var newSize = sizeThatFits(constrainedSize)
newSize.width = min(newSize.width, constrainedSize.width)
newSize.height = min(newSize.height, constrainedSize.height)
self.size = newSize
return newSize
}
The idea is to use sizeThatFits with a constrained size to determine the size to adopt and then use the min expected width/height.
Then you just have to do something like
yourLabel.sizeToFit(self.contentView.size) // or
yourLabel.sizeToFit(CGSize(width: maxWidth, height: self.contentView.size.height))
You can set preferred Width property of label. That worked for me
preferred Width

Resources