I'm using the following method implementation to calculate the height of a UITableViewCell which is containing multiline text:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 1 && indexPath.row == 1) {
NSDictionary *fields = self.messageDetailsDictionary[#"fields"];
NSString *cellText = fields[#"message_detail"];
UIFont *cellFont = [UIFont systemFontOfSize:14.0];
CGSize constraintSize = CGSizeMake(250.0f, MAXFLOAT);
CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
return labelSize.height + 20;
} else {
return tableView.rowHeight;
}
}
For completeness, here's the cellForRowAtIndexPath entry for this cell:
UITableViewCell *cell = [[UITableViewCell new] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"DetailCell"];
if (cell == nil) {
cell = [[UITableViewCell new] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"DetailCell"];
}
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
cell.textLabel.font = [UIFont systemFontOfSize:14.0];
NSDictionary *fields = self.messageDetailsDictionary[#"fields"];
cell.textLabel.numberOfLines = 0; // This means multiline
cell.textLabel.text = fields[#"message_detail"];
return cell;
The UITableViewCell is in a Grouped UITableView, that's important because it affects the width of the cell.
This is working to the extent that it does calculate a cell height that is large enough to accomodate the text being input, but it seems to be a little too large as there is a little too much space at the top and bottom of the cell. This is dependent on the quantity of text so I don't think it's related to the return labelSize.height + 20; statement. I suspect this is down to the '250.0f' value I'm using in CGSizeMake but I don't know what the correct value should be here.
Ultimately what I want is to have a cell that has consistent padding above and below the text for any content size.
Can anyone help?
Through a process of elimination it turns out that the magic number is 270.0f.
The width of the tableView frame can be obtained from self.tableView.frame.size.width. This is 320.0f and taking 50.0f from this (equals to 270.0f) seems to produce consistent results.
So the method should be as follows:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 1 && indexPath.row == 1) {
NSDictionary *fields = self.messageDetailsDictionary[#"fields"];
NSString *cellText = fields[#"message_detail"];
UIFont *cellFont = [UIFont systemFontOfSize:14.0];
CGSize constraintSize = CGSizeMake(self.tableView.frame.size.width - 50.0f, MAXFLOAT);
CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
return labelSize.height + 20.0f;
} else {
return tableView.rowHeight;
}
}
I'm not exactly sure why 50.0f is the correct value, because I'm not sure how much of that 50.0f is the distance from the cell border to the tableView edge and how much of it is internal padding within the cell itself but it works unless you've amended either of those two values.
Related
I have a functionality where when the user clicks on a cell, the cell expands to display more labels. The height is recalculated during expansion and is based on the size of my very first label or 2nd label on the cell whichever is greater.
Below is my code for height calculation:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGSize labelSize;
if ([self.expandedCells containsObject:indexPath]) {
// Calculate the cell height based on the address details or branch name which ever is greater.
UIFont *cellFont = [UIFont fontWithName:#"Helvetica" size:17.0];
BranchDetails *branchDetail = ((BranchDetails *)[self.branchDetails objectAtIndex:[indexPath row]]);
NSString *addressText = branchDetail.addressDetails;
CGSize addressLabelSize = [addressText boundingRectWithSize:CGSizeMake(tableView.frame.size.width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:cellFont} context:nil].size;
NSString *branchNameText = branchDetail.branchName;
CGSize branchLabelSize = [branchNameText boundingRectWithSize:CGSizeMake(tableView.frame.size.width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:cellFont} context:nil].size;
labelSize = (addressLabelSize.height > branchLabelSize.height) ? addressLabelSize : branchLabelSize;
}
else {
NSString *cellText = [self.branchList objectAtIndex:[indexPath row]];
UIFont *cellFont = [UIFont fontWithName:#"Helvetica" size:17.0];
labelSize = [cellText boundingRectWithSize:CGSizeMake(tableView.frame.size.width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:cellFont} context:nil].size;
}
CGFloat cellHeight = labelSize.height + 20;
return [self.expandedCells containsObject:indexPath] ? cellHeight * 4 : cellHeight;
}
Choppy label cell image:
I tried adding a scroll view while creating a cell in cellForRowAtIndexPath so user can see all the details on the cell. Code for scroll view:
UIScrollView *scrollView=[[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, 320, 100)];
[cell.contentView addSubview:scrollView];
This still adds the scroll on the table and not within the cell.
Is there a better way for me to either calculate the height, so all the labels within the cell are displayed or add a scroll inside the cell so the user can scroll within each cell after expansion to see all the details?
A better way to do this is to reiterate all UILabels in that cell and then set the height as the sum of all those views heights + gaps.
Something like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat rowHeight = 0;
if ([self.expandedCells containsObject:indexPath]) {
CGFloat totalHeight = 0;
YourCell *cell = [tableView cellForRowAtIndexPath:indexPath];
for(UIView *v in [cell.contentView subviews]) {
if([v isKindOfClass:[UILabel class]]) {
UILabel *lbl = (UILabel*)v;
totalHeight += lbl.frame.size.height;
}
}
rowHeight = totalHeight;
}
else {
rowHeight = 25; // a fixed height for unexpanded rows
}
return rowHeight;
}
This will not work exactly as you want but you can get the idea how to work around it.
I am using Autolayout and have a customised UITableViewCell that contains UITextView. The height of the cell and text view should be dynamically resized to accomodate larger contents.
Following answers from these links resize ui text view to its content and
dynamic ui text view, I have used a height constraint on the text view and set its value to the height of the content. This does the job of accommodating the textView, but it is overshooting the cell. I have tried options like adjusting the size of the cell also but none of them have helped. Surprisingly, sizeToFit seems to have no effect.
Here is the code I am using:
+ (SongAdditionalTextCell *)resize:(SongAdditionalTextCell *)additionalTextCell{
UITextView *textView = additionalTextCell.additionalText;
CGSize sizeThatFitsTextView = [textView sizeThatFits:CGSizeMake(textView.frame.size.width, MAXFLOAT)];
NSLog(#"content height is %f, frame height is %f", textView.contentSize.height, textView.frame.size.height );
additionalTextCell.addnlTextHeightConstraint.constant = sizeThatFitsTextView.height;
return additionalTextCell;
}
Please see the attached image of the view (text view is second cell). any suggestions appreciated?
I use this one:
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 2)
{
NSString *cellText = #"init some text";
CGSize labelSize = [self calculateTextSize:cellText];
return labelSize.height + 20.0f;
}
return 45;
}
- (CGSize) calculateTextSize: (NSString*) text
{
UIFont *cellFont = [UIFont fontWithName:#"HelveticaNeue" size:12.0];
CGFloat width = CGRectGetWidth(self.tableView.frame) - 40.0f;
CGSize constraintSize = CGSizeMake(width, MAXFLOAT);
CGRect labelRect = [cellText boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:cellFont} context:NSLineBreakByWordWrapping];
CGSize labelSize = labelRect.size;
return labelSize;
}
CGFloat width = CGRectGetWidth(self.tableView.frame) - 40.0f; -
Calculation of the maximum available width of the text, you can use self.view.frame or etc. - 40.0f - because i have indentation from the edge = 20.0f
Try this it working for me
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGSize labelSize = [[[Array objectAtIndex:indexPath.row] valueForKey:#"detail"] sizeWithFont:[UIFont boldSystemFontOfSize:10] constrainedToSize:CGSizeMake(255, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];
return labelSize.height +15;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpletableidentifier=#"simpletableidentifier";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:simpletableidentifier];
if(cell == nil)
{
cell =[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:simpletableidentifier];
CGSize labelSize = [[[Array objectAtIndex:indexPath.row] valueForKey:#"detail"] sizeWithFont:[UIFont boldSystemFontOfSize:10] constrainedToSize:CGSizeMake(255, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];
UILabel *desc=[[UILabel alloc]initWithFrame:CGRectMake(35,2,245,labelSize.height)];
desc.backgroundColor=[UIColor clearColor];
desc.tag=2;
desc.numberOfLines=0;
desc.lineBreakMode=NSLineBreakByWordWrapping;
desc.font=[UIFont boldSystemFontOfSize:10];
desc.textColor=[UIColor whiteColor];
[cell.contentView addSubview:desc];
}
NSString *Str3 = [[Array objectAtIndex:indexPath.row] valueForKey:#"detail"];
UILabel *desc=(UILabel *)[cell.contentView viewWithTag:2];
desc.text=Str3;
cell.selectionStyle=UITableViewCellSelectionStyleNone;
cell.backgroundColor=[UIColor clearColor];
cell.contentView.backgroundColor=[UIColor clearColor];
return cell;
}
Finally figured it out. #RoryMcKinnel's comment does help. Here's what I did in case it can help others. The problem was that I had two type of cells, one where I need to adjust the height (additionalText) and one where I do not need to do that.
In the raw version, I override both estmatedHeightForRowAtIndexPath and heightForRowAtIndexPath and in both methods if the index is for row which is additionalText, I send UITableViewAutomaticDimension else I send the frame height. Of course refinements can be done :) to send better values, but the core concept is same.
here is the code.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.additionalTexts[#((long) indexPath.row)] != nil){
return UITableViewAutomaticDimension;
} else return [[SongTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MainCellIdentifier].frame.size.height;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.additionalTexts[#((long) indexPath.row)] != nil){
return UITableViewAutomaticDimension;
} else return [[SongTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MainCellIdentifier].frame.size.height;
}
and the resulting screen looks like
I have followed the instructions I have been able to find on stackoverflow to fix the following but none have worked. Below is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"DoCCell";
DoCCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[DoCCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
CGSize constraintSize = CGSizeMake(cell.infoLabel.frame.size.width, MAXFLOAT);
CGSize labelSize = [_content[[indexPath row] * 2] sizeWithFont:[UIFont fontWithName:#"HelveticaNeue-Light" size:12.0f]
constrainedToSize:constraintSize
lineBreakMode:NSLineBreakByWordWrapping];
CGRect frame = CGRectMake (cell.infoLabel.frame.origin.x, cell.infoLabel.frame.origin.y, labelSize.width, labelSize.height);
[cell.infoLabel setFrame:frame];
cell.infoLabel.lineBreakMode = NSLineBreakByWordWrapping;
cell.infoLabel.numberOfLines = 10;
_font = cell.infoLabel.font;
cell.infoLabel.text = _content[[indexPath row] * 2];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT);
CGSize size = [_content[[indexPath row] * 2] sizeWithFont:[UIFont fontWithName:#"HelveticaNeue-Light" size:12.0f]
constrainedToSize:constraintSize
lineBreakMode:NSLineBreakByWordWrapping];
return size.height + 30.0;
}
but when I run my code the height of the cells is changed appropriately while the label size is not.
The cell is a custom cell and I have added the label via the .xib file. I tried manually stretching the label which worked in the sense that it would display all of the text so the issue is not in the wrapping of the label. I have also tested the cell.infoLabel.frame.size.height and the height value DOES change with the height of the cell as far as the value is concerned but it is not displayed as such. What am I doing wrong?
I think that the problem might be of adjusting the vertical alignment of the text in the UILabel instance.
I think you should do the following inside cellForRowAtIndexPath:
cell.infoLabel.text = _content[[indexPath row] * 2];
cell.infoLabel.numberOfLines = 0;
[cell.infoLabel sizeToFit];
More details about it can be found in the following link: Vertical Alignment of UILabel Text
Hope this helps! :)
Use below code in cellForRowAtIndexPath delegate of tableView :
// getDynamicHeight function calculates height based on text length
//Dynamically determine height for cell text
CGFloat calculatedHeight = [self getDynamicHeight:#"Some very long text here"];
//Set name label frame based on text height
cell.infoLabel.frame = CGRectMake(cell.infoLabel.frame.origin.x,cell.infoLabel.frame.origin.y, cell.infoLabel.frame.size.width,calculatedHeight);
cell.infoLabel.numberOfLines = 5;
cell.infoLabel.text = #"Some very long text here";
// getDynamicHeight function
//Dynamically determine height for cell based in containing Text
-(CGFloat) getDynamicHeight : (NSString *) strCellTextName {
UILabel *lblGetDynamicHeight = [[UILabel alloc] init];
lblGetDynamicHeight.lineBreakMode = UILineBreakModeWordWrap;
lblGetDynamicHeight.text = strCellTextName;
CGSize labelStringSize = [lblGetDynamicHeight.text sizeWithFont:lblGetDynamicHeight.font constrainedToSize:CGSizeMake(142, 9999) lineBreakMode:lblGetDynamicHeight.lineBreakMode];
return labelStringSize.height; }
I have a UITableViewCell that is dynamically sized based on the content in it. I do this in heightForRowAtIndexPath as follows:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 1 && indexPath.row == 1) {
NSDictionary *fields = self.messageDetailsDictionary[#"fields"];
NSString *cellText = fields[#"message_detail"];
UIFont *cellFont = [UIFont systemFontOfSize:14.0];
CGSize constraintSize = CGSizeMake(self.tableView.frame.size.width - 50.0f, MAXFLOAT);
CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
return labelSize.height + 20.0f;
} else {
return tableView.rowHeight;
}
}
In cellForRowAtIndexPath I customise the cell as follows:
case 1:{
UITableViewCell *cell = [[UITableViewCell new] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"DetailCell"];
if (cell == nil) {
cell = [[UITableViewCell new] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"DetailCell"];
}
NSDictionary *fields = self.messageDetailsDictionary[#"fields"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
cell.textLabel.font = [UIFont systemFontOfSize:14.0];
cell.textLabel.numberOfLines = 0; // This means multiline
cell.textLabel.text = fields[#"message_detail"];
return cell;
}
This all works fine, however I want the content of the cell to detect phone numbers, dates/times etc. So I think I need to have the content in a UITextView within the UITableViewCell to make this possible.
How can I dynamically size the UITextView and add it to the cell with the content by modifying my code above? Sample code please with answers because I've already tried adding the UITextView as a subview of the cells contentView but it's not working as I'd expect.
you can use the sizeToFit or sizeThatFits: methods to resize the UITextView object
While creating cell, you need to add textView as its subView and no need to set textView size separately. You just need add margin values while returning height for row in
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
I have a list view that sometimes has long text. When the text is a few paragraphs, it tends to get cut off at some point. Part of the strange behavior is that it does not always get cut off at the same character, but kind of random.
Here is the code that I use to populate the list:
// CREATING EACH CELL IN THE LIST
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//static const NSInteger kLabelTag = 1;
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault)
reuseIdentifier:#"business"];
NSString *comment = [[items_array objectAtIndex:[indexPath row]] objectForKey:(#"comment")];
NSString *first_name = [[items_array objectAtIndex:[indexPath row]] objectForKey:(#"first_name")];
//Boolean *is_private = [[items_array objectAtIndex:[indexPath row]] objectForKey:(#"is_private")];
// Creating a constraint size for the label you are making
CGSize constraint = CGSizeMake(320 - (10 * 2), 20000.0f);
// Determining the size that you will need for the label based on the comment
// length and the contraint size
CGSize size = [comment sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
// Creating the label and initializing it with the frame that you have
// determined by your size calculations above
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, MAX(size.height, 44.0f) + 20.0f)];
// setting up the label properties
label.numberOfLines = 0;
label.lineBreakMode = UILineBreakModeWordWrap;
standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSString *is_private = [standardUserDefaults objectForKey:#"is_private"];
if ( is_private == nil )
{
label.text = [first_name stringByAppendingString:[#": " stringByAppendingString:comment]]; // comment;
}
else
if ( [is_private isEqualToString:#"0"])
{
label.text = [first_name stringByAppendingString:[#": " stringByAppendingString:comment]]; // comment;
}
else
{
label.text = comment;
}
// adding the label view to the cell that you have created
[cell.contentView addSubview:label];
// CLOSE THE SPINNER
[spinner stopAnimating];
// return the cell for the table view
return cell;
}
//This method will determine how tall each row needs to be. Cell size for word wrapping.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
NSString *comment = [[items_array objectAtIndex:[indexPath row]] objectForKey:(#"comment")];
// Again you are getting the constraints because you are going to us this size
// to constrain the height of the cell just like you determined the size for the label.
CGSize constraint = CGSizeMake(320 - (10 * 2), 20000.0f);
// Again, determining the size that we will need for the label, because it will drive
// the height of the cell
CGSize size = [comment sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
// Finally, we can determine the height for the cell!
CGFloat height = MAX(size.height, 44.0f);
// return the height, which is the height of the label plus some extra space to make
// it look good.
return height + (10 * 2);
}
Does this code have anything particularly incorrect with it? Or is the problem possibly rooted elsewhere?
Thanks!
when you create the label, you hand it a max height
MAX(size.height, 44.0f)
which means the label is never higher than 44 points.
If the text (which is being wrapped to fit the width of the label) is higher than the label, the lines that fallow below it will still be in the label, but not visible.
Your options :
-Make the label font auto sizing
-change the height, or switch to dynamic height for the label
this would go something like this every time you update it:
myLabel.text = #"some text";
CGRect rect = myLabel.frame;
rect.size.height = textView.contentSize.height;// Adding.size Since height is not a member of CGRect
textView.frame = rect; //your label is now as high as its contents (the text)
-trim the text that goes into the label so it never exceeds the height of the label.
I would get rid of having your own UILabel unless absolutely necessary and dynamically adjust the cell's height based on the string size. Check out my example below:
Create the strings before loading tableview
-(void)createStrings{
newArray = [NSMutableArray array];
for(NSDictionary *dictionary in items_array){
NSString *comment = [dictionary objectForKey:(#"comment")];
NSString *first_name = [dictionary objectForKey:(#"first_name")];
NSString *combinedString = [first_name stringByAppendingString:[#": " stringByAppendingString:comment]];
[newArray addObject:combinedString];
}
}
#pragma mark - Table View Data Source
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
static NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:17];
}
cell.textLabel.text = [newArray objectAtIndex:indexPath.row];
return cell;
}
#pragma mark - Table View Delegate
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
UIFont *cellFont = [UIFont fontWithName:#"Helvetica" size:17];
CGSize constraintSize = CGSizeMake(self.tableView.frame.size.width, MAXFLOAT);
CGSize labelSize = [[newArray objectAtIndex:indexPath.row] sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
return labelSize.height + 30;
}