Changing UILabel frame height inside tableView cell - ios

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.

Related

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

UItableviewcontroller SizeToFit not working on UILabel field

Hi I am using sizetofit on my UILabel field, it works fine when I load the tableview controller, but then starts cutting of letters and wrapping to another line when I scroll up and down. Here is the code I use in CellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"subjectCell" forIndexPath:indexPath];
// Configure the cell...
// Load and display subject photo image
UIImageView *subjectImageView = (UIImageView *)[cell viewWithTag:200];
subjectImageView.image = [UIImage imageNamed:subjectImages[indexPath.row]];
// Load and display subject label
UILabel *subjectLabel = (UILabel *)[cell viewWithTag:201];
subjectLabel.text = [subjectItems objectAtIndex:indexPath.row];
[subjectLabel sizeToFit];// call this to fit size of the label according to text
return cell;
}
And here is a screen shot of my tableviewcontroller originaly loaded:
And here after scrolling up and down you can see the descriptions have been cut
Thanks for any help you can give to solve this.
Cheers
Carson
Using sizeToFit for UILabel can cause a mess like this in UITableView with reusing cells. One of solutions I can think of is to set the width of the frame for your label width before you call sizeToFit and you have to do this every time you assign text to the label. It should look something like this:
CGRect frame = subjectLabel.frame;
frame.size.width = 100.0; //try to adjust this value
subjectLabel.frame = frame;
[subjectLabel sizeToFit];

Wrong UITableViewCell height with UITableViewAutomaticDimension

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

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

Calling heightForRowAtIndexPath from within cellForRowAtIndexPath?

So, it seems that heightForRowAtIndexPath is called before cellForRowAtIndexPath. I'm currently using cellForRowAtIndexPath to calculate the height of each cell (they're variable heights, using a UILabel with varying amounts of text in it.
I'm puzzled as to exactly how I can set the cell's height correctly if the method that sets the height is called before the method that calculates it.
I've created a category of NSString, and another of UILabel, which working together make the UILabel the right size. The issue is that the UITableViewCell instances which contain those labels aren't being resized, so the labels hang over the end of each label, overlapping the ones beneath.
I know that what I need to do is make the height of the cell equal the height of the label, plus some extra margin space, but I'm puzzled as to how I can set the height before the label height has been calculated. Can I set the height someone without using heightForRowAtIndexPath? Or if not, could I calculate the height of the cell elsewhere? I'm currently using indexPath.row to make that happen, so doing it beforehand would be tricky. Here's the code my cellForRowAtIndexPath:
cellForRowAtIndexPath
- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath
{
FQCustomCell *_customCell = [tableView dequeueReusableCellWithIdentifier: #"CustomCell"];
if (_customCell == nil) {
_customCell = [[FQCustomCell alloc] initWithStyle: UITableViewCellStyleValue1 reuseIdentifier: #"CustomCell"];
}
_customCell.myLabel.text = [[_loadedNames objectAtIndex: indexPath.row] name];
_customCell.myLabel.font = [UIFont fontWithName: #"FontA" size: 15.0f];
UIView *backView = [[UIView alloc] initWithFrame: CGRectZero];
backView.backgroundColor = [UIColor clearColor];
_customCell.backgroundView = backView;
_customCell.myLabel.frame = CGRectMake(5, 5, 265, 65);
[_customCell.myLabel sizeToFitMultipleLines];
return _customCell;
}
You need to calculate the height of the cell in heightForRowAtIndexPath, something like
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *theText=[[_loadedNames objectAtIndex: indexPath.row] name];
CGSize labelSize = [theText sizeWithFont:[UIFont fontWithName: #"FontA" size: 15.0f] constrainedToSize:kLabelFrameMaxSize];
return kHeightWithoutLabel+labelSize.height;
}
You can set some constraints on the label size by setting kLabelFrameMaxSize
#define kLabelFrameMaxSize CGSizeMake(265.0, 200.0)
Then you return the height by adding a constant height with the variable label height.
Also, to be consistent, you should use the same methodology to set the frame in cellForRowAtIndexPath instead of using sizeToFitMultipleLines
- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath
{
FQCustomCell *_customCell = [tableView dequeueReusableCellWithIdentifier: #"CustomCell"];
if (_customCell == nil) {
_customCell = [[FQCustomCell alloc] initWithStyle: UITableViewCellStyleValue1 reuseIdentifier: #"CustomCell"];
}
_customCell.myLabel.text = [[_loadedNames objectAtIndex: indexPath.row] name];
_customCell.myLabel.font = [UIFont fontWithName: #"FontA" size: 15.0f];
UIView *backView = [[UIView alloc] initWithFrame: CGRectZero];
backView.backgroundColor = [UIColor clearColor];
_customCell.backgroundView = backView;
CGSize labelSize = [_customCell.myLabel.text sizeWithFont:_customCell.myLabel.font constrainedToSize:kLabelFrameMaxSize];
_customCell.myLabel.frame = CGRectMake(5, 5, labelSize.width, labelSize.height);
return _customCell;
}
You should calculate the height of the cell in heightForRowAtIndexPath method. It should be done without using any UILabel, just like heightForRowAtIndexPath implementation in JP Hribovsek's answer.
Then in cellForRowAtIndexPath do not set the frame of any subviews and do not call sizeToFitMultipleLines method of the label. Instead, implement layoutSubviews method of FQCustomCell such that the subview frames are set correctly for any possible bounds of the cell. You should't care about the text or font size in this method, just make sure the margins of the label are correct. If the height of the cell is calculated correctly in heightForRowAtIndexPath method, the size of your labels will be just right.
For every cell heightforRowAtIndexPath is called first and then CellForRowAtIndexPath . So you can calculate the height first and then fill up the data in CellForRowAtIndexPath for every cell.

Resources