Data from another UITableViewCell is repeated in current cell - iphone

When my custom UITableViewCell is reused, all of the data does not get reset for the current cell. Data from the reused cell is inserted for a particular UILabel.
I have a tableview of Dishes. Dishes have Comments. I'd like to display comments for each dish within a tableviewcell. Depending on how many comments there are, I only make that many comment labels in each cell. However, when a cell is reused, the wrong comment - a comment from a different Dish - shows up.
I am using CoreData and a Dish has many Comments and let's just say I only want to display the first two comments.
I have created two UILabels in IB with heights and width of zero so that it resizes to how long each comment is.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
DishCell *cell = [tableView dequeueReusableCellWithIdentifier:#"DishCell"];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{ ...I put all of the Dishes in a NSFetchedResultsController instance so *dish is a NSManagedObject that has many comments
NSSet *commentsSet = dish.comments;
NSSortDescriptor *sortByDate = [[NSSortDescriptor alloc]initWithKey:#"date" ascending:YES];
NSArray *commentArray = [commentsSet sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortByDate]];
if (commentsSet.count< 1) {
dishCell.comment1label.text = #"";
dishCell.comment2label.text = #"";
}else {
Comment *comment = [commentArray objectAtIndex:0];
NSString *commenterName = comment.displayName;
NSString *commentString = comment.commentText;
NSString *text = [NSString stringWithFormat:#"%#: %#", commenterName, commentString];
CGSize comment1Size = [text sizeWithFont:[UIFont systemFontOfSize:10.0f] constrainedToSize:CGSizeMake(constraint.width - dishCell.commentBubble.frame.size.width + 4, constraint.height) lineBreakMode:UILineBreakModeWordWrap];
//Ignore this setFrame - it works
[dishCell.comment1label setFrame:CGRectMake(32, faveIconYpos - 2 + dishCell.favIcon.frame.size.height, 298 - dishCell.commentBubble.frame.size.width, comment1Size.height + 2)];
dishCell.text = text;
if (commentsSet.count > 1) {
Comment *comment2 = [commentArray objectAtIndex:1];
[dishCell.comment2label setTextColor:[UIColor whiteColor]];
[dishCell.comment2label setFont:[UIFont systemFontOfSize:12.0]];
NSString *text2 = [NSString stringWithFormat:#"%#: %#", comment2.displayName, comment2.commentText];
CGSize comment2Size = [text2 sizeWithFont:[UIFont systemFontOfSize:10.0f] constrainedToSize:CGSizeMake(constraint.width - dishCell.commentBubble.frame.size.width + 4, constraint.height) lineBreakMode:UILineBreakModeWordWrap];
[dishCell.comment2label setFrame:CGRectMake(32, faveIconYpos + dishCell.favIcon.frame.size.height + dishCell.testcom.frame.size.height - 2, 298 - dishCell.commentBubble.frame.size.width, comment2Size.height + 2)];
dishCell.comment2label.text = text2;
}
I've parsed out some of the code, but the main thing is that data from another cell gets inserted into my labels. Why is this happening?

Related

Ugly heightForRowAtIndexPath and cellForRowAtIndexPath

I have a UItableview, which I'm populating with data, using heightForRowAtIndexPath and cellForRowAtIndexPath. Apparently Apple makes me do things in my code twice.
First I have to calculate the size of my views (for that I have to make them) in heightForRowAtIndexPath and then I have to make them again, to add them to the actual view.
I have a pretty complicated view, so it looks double ugly, when you have to write it twice.
Isn't there a better way to do this?
UPDATE
This is how my code looks. It's not totally the same, but pretty close. Why in the world does apple make me write this twice?
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"heightForRowAtIndexPath");
//Initiating strings
NSString *headlineString;
NSString *subHeadlineString;
NSString *bylineString;
if (global.magazine.issues.count==0) {
return 45;
}else if(indexPath.section == global.magazine.issues.count+1) {
//Finding the right issue and article for this row
Issue *issue = [global.magazine.issues objectAtIndex:global.magazine.issues.count-1];
//Creating the headline
headlineString = [NSString stringWithFormat:#"<span class='bold_style'>FOREWORD</span>"];
//Creating the subHeadline
subHeadlineString = [NSString stringWithFormat:#"%#", [issue.magazine_foreword substringToIndex:100]];
//Creating byline
bylineString = [[NSString stringWithFormat:#"<span class='ital_style'>By %#</span>", issue.magazine_byline] capitalizedString];
}else{
//Finding the right issue and article for this row
Issue *issue = [global.magazine.issues objectAtIndex:indexPath.section-1];
Article *article = [issue.articles objectAtIndex:indexPath.row];
//Creating the headline
headlineString = [NSString stringWithFormat:#"<span class='bold_style'>%#</span>", [article.title uppercaseString]];
//Creating the subHeadline
subHeadlineString = [NSString stringWithFormat:#"%#", [article.main_text substringToIndex:100]];
//Creating byline
bylineString = [NSString stringWithFormat:#"<span class='ital_style'>By %#</span>", article.byline];
}
//Creating the labels
NMCustomLabel *headline = [global.label headLineLabelWithString:headlineString fromTop:30 withWidth:global.screenWidth-60];
NMCustomLabel *subHeadline = [global.label subHeadlineLabelWithString:subHeadlineString fromTop:30+headline.height+10 withWidth:global.screenWidth-60];
NMCustomLabel *byline = [global.label articleBylineLabelWithString:bylineString fromTop:30+headline.height+10+subHeadline.height+10 withWidth:global.screenWidth-60];
//Setting the height of the row
return 30+headline.height+10+subHeadline.height+10+byline.height+30;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"cellForRowAtIndexPath");
//Preparing the cell
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
//Removing former text views
for (UIView *subview in [cell subviews]) {
if (subview.tag == 21 || subview.tag == 22 || subview.tag == 23) [subview removeFromSuperview];
}
//Removing and setting tableview border
[[cell viewWithTag:30] removeFromSuperview];
UIView *rightBorder = [[UIView alloc] initWithFrame:CGRectMake(cell.width-1, 0, 1, cell.height)];
rightBorder.backgroundColor = global.lightGrey;
rightBorder.tag = 30;
[cell addSubview:rightBorder];
//Setting the seletion background color on the cells
UIView *bgColorView = [[UIView alloc] init];
bgColorView.backgroundColor = global.extraLightGrey;
cell.selectedBackgroundView = bgColorView;
if (global.magazine.issues.count==0) {
return cell;
}else if (indexPath.section-1 == global.magazine.issues.count) {
//Finding the right issue and article for this row
Issue *issue = [global.magazine.issues objectAtIndex:global.magazine.issues.count-1];
//Creating the headline
NSString *headlineString = [NSString stringWithFormat:#"<span class='bold_style'>FOREWORD</span>"];
NMCustomLabel *headline = [global.label headLineLabelWithString:headlineString fromTop:30 withWidth:global.screenWidth-60];
headline.tag = 21;
[cell addSubview:headline];
//Creating the subHeadline
NSString *subHeadlineString = [[NSString stringWithFormat:#"%#", issue.magazine_foreword] substringToIndex:100];
NMCustomLabel *subHeadline = [global.label subHeadlineLabelWithString:subHeadlineString fromTop:30+headline.height+10 withWidth:global.screenWidth-60];
subHeadline.tag = 22;
[cell addSubview:subHeadline];
//Creating byline
NSString *bylineString = [[NSString stringWithFormat:#"<span class='ital_style'>By %#</span>", issue.magazine_byline] capitalizedString];
NMCustomLabel *byline = [global.label articleBylineLabelWithString:bylineString fromTop:30+headline.height+10+subHeadline.height+10 withWidth:global.screenWidth-60];
byline.tag = 23;
[cell addSubview:byline];
}else{
//Finding the right issue and article for this row
Issue *issue = [global.magazine.issues objectAtIndex:indexPath.section-1];
Article *article = [issue.articles objectAtIndex:indexPath.row];
//Creating the headline
NSString *headlineString = [NSString stringWithFormat:#"<span class='bold_style'>%#</span>", [article.title uppercaseString]];
NMCustomLabel *headline = [global.label headLineLabelWithString:headlineString fromTop:30 withWidth:global.screenWidth-60];
headline.tag = 21;
[cell addSubview:headline];
//Creating the subHeadline
NSString *subHeadlineString = [NSString stringWithFormat:#"%#", [article.main_text substringToIndex:100]];
NMCustomLabel *subHeadline = [global.label subHeadlineLabelWithString:subHeadlineString fromTop:30+headline.height+10 withWidth:global.screenWidth-60];
subHeadline.tag = 22;
[cell addSubview:subHeadline];
//Creating byline
NSString *bylineString = [[NSString stringWithFormat:#"<span class='ital_style'>By %#</span>", article.byline] capitalizedString];
NMCustomLabel *byline = [global.label articleBylineLabelWithString:bylineString fromTop:30+headline.height+10+subHeadline.height+10 withWidth:global.screenWidth-60];
byline.tag = 23;
[cell addSubview:byline];
}
return cell;
}
The easiest solution is to follow DRY principles and either add height as a property of the objects you are using as a datasource or add a method to your view controller such as:
-(CGFloat)calculateHeightForHeadline:(NSString*)headline andSubHeadline:(NSString*)subHeadline andByLine:(NSString*)byLine
Then at least you only have the calculation code in one place.
Alternatively, you could call [tableView heightForRowAtIndexPath:indexPath] from your cellForRowAtIndexPath method
You have to do it twice because the table view needs to know how tall it is in total before it draws anything - so it calls the height method for every row before it calls any cell method. With your current code, depending on the number of rows, you may be experiencing a slight delay before the table appears - instruments will show you that it is the height method you're spending time in.
I don't know what you custom label classes do but you may be able to calculate the height without having to create views (which is expensive) by using the string or attributed string drawing and size calculation UIKit extensions, which were created for this exact purpose.
heightForRowAtIndexPath
Will be called for all your rows. This delegate method returns the height for your rows. As your design required different height for each rows depending upon the condition. Unfortunately you'll have to calculate the row height each time before your row gets created. It's like we are deciding just before drawing the row what will be it's hight.
There's one way you can avoid this. But I won't advice you to go for it. Because it'll change the design of your UITableView. What you can do is you can decide the maximum height of your row out of all the possible conditions you have. For eg. Let's consider it as 100 pixel. Then you can draw your rest of the cells. However, that'll leave empty space if any of your row is less then 100 pixel. And it'll look shaggy.
Basically, to meet your requirement you'll have to do this twice. No other choice :-(
I wonder, why are you creating your custom labels in heightForRowAtIndexpath? Why don't you just calculate the size of the text with sizeWithFont: or such methods? I think that would be a better way to calculate height of the row. Good Luck!

Expanded UITableViewCell according to UILabel size

I have a UITableView with 4 UILabel's: Title, Body, Author and Date, he looks like this:
What I want to accomplish is, when user click on the cell itself, another label should be added to the cell, the "Body" label and the cell should expand according to this label size.
Something like this:
How can I do that? I've searched stackoverflow, tried some code pieces, but still didn't found the right solution.
Thanks!
Edit 1: 14.11.12 at 14:52
I managed to change the size of the UILabel with the current text:
- (CGRect )resizeLabelByFontSize:(UILabel *)customCellLabel withMaxHeightSize:(CGFloat )maxHeight
{
CGSize maximumLabelSize = CGSizeMake(239, maxHeight);
CGSize expectedLabelSize = [customCellLabel.text sizeWithFont:customCellLabel.font constrainedToSize:maximumLabelSize lineBreakMode:customCellLabel.lineBreakMode];
//adjust the label the the new height.
CGRect newFrame = customCellLabel.frame;
newFrame.size.height = expectedLabelSize.height;
return newFrame;
}
But how can I change the size of the cell according to the size of the new UILabel?
By seeing Images in Question
Here is the method which just create the Dynamic FRAME for UILabel have a look at this
By getting the Height and Width for UIlabel you can calculate the Whole height and could set the Row Height of UITableView.
- (void)setLabeltextWithVerticalAlignTop:(NSString *)theText
{
CGSize labelSize;
// here labelSize is hard-wired but could use constants to populate the size
labelSize = CGSizeMake(210, 129);//this is just for example
//now create the Size from textString SO that We could assign this size to the Label.
CGSize theStringSize = [theText sizeWithFont:lblTitle.font constrainedToSize:labelSize lineBreakMode:lblTitle.lineBreakMode];
lblTitle.frame = CGRectMake(lblTitle.frame.origin.x, lblTitle.frame.origin.y, theStringSize.width, theStringSize.height);
lblTitle.text = theText;
}
Call Above Method For setting the height and Width of description Label you need to pass the text to be shown on that description label.
As you gets the height for that Label, Now On the Basis of this You can Adjust the heigh of Row of TableView.
EDIT:Above Code Just Create the Dynamic Frame For The UILabel
You should take a view of this this is what you looking for....!!!.here you would find a sample code too.
EDIT:As you edited your Question see ,it just the logic which you need to convert it into runnable code here it is.
Use Below Method in Your Code called for each row, and make some calculation inside it.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat rowHeight=0.0;
//here it seems cell have 4 subview added on it.
//so if you could calculate the totla hieht of them.
//so what you really need to do.you just use hieght calculative Method for getting hieght of each of three UILabel
//you need to modify `setLabeltextWithVerticalAlignTop` method .
rowHeight= [self setLabeltextWithVerticalAlignTop:#"pass the correspondingText"];// suppose it returns some hieght for FisrtLabel.
//suppoose here you get the 20.0 height here
rowHeight= rowHeight+[self setLabeltextWithVerticalAlignTop:#"pass the correspondingText"];
// suppose it returns some hieght for secondUIlabel.
//suppoose here you get the 40.0 height here
rowHeight= rowHeight+ [self setLabeltextWithVerticalAlignTop:#"pass the correspondingText"];
// suppose it returns some hieght for ThirdUIlabel.
// suppoose here you get the 15.0 height here
//here you have totla height you just need to add some gapping floating value for all of three UIlabel.so that the could not overlap like as.
rowHeight= rowHeight+20.0;
//now you can return that total height
return rowHeight;
}
Note:This is just logic you need to convert it into runnable code.i am sure this can help.
I hope it may help you.
Implement the following methods
– (void) tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// cast cell, add label, expand labels etc
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [indexPath isEqualTo:[tableView indexPathForSelectedRow]] ? /* expanded height */ : 80 /* normal height */;
}
If you want the row to stay selected even after another row is selected then add a custom BOOL property to your custom cell, e.g. expanded, and use that to determine the height.
You can use tableView:didSelectRowAtIndexPath
In that method you can then create your code to unhide the body label, adjust the relative positions of everything else. Calculate the new height of the row and then call the Table View's reloadRowsAtIndexPath: withRowAnimation: method.
Sorry if there's not a lot of detail in that, but hopefully that should get you on the right track.
Ok, firstly... To expand you need something like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
Now here is the catch:
You should calculate the size of the UITableViewCell (expanded and non-expanded)
Doing so when you are actually scrolling might be expensive and will give you a bad experience
My advice:
Calculate both sides, before you have actually conclude to build the UITableView, since you want to have dynamic sizes. If you don't and all cells will have the same size expanded, you can use what lammmert said.
NSIndexPath *selectedRow;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
selectedRow = indexPath;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath == selectedRow){
//return your custom value
}
return 100;
}
I think it will look something like that
So, in order to do this, using expended UITableViewCell, i've created 2 different custom cells, at start the table is showing the first cell, when I click on the cell, the table is showing the second one. Its that easy - yeah!
So I have the UIViewController with the UITableView that implements the table delegate methods:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([self.selectedCellIndexPath isEqual:indexPath])
{
return [self expandedCellHeight:indexPath];
}
else
{
return kRegularCellHeight;
}
}
-(CGFloat)expandedCellHeight:(NSIndexPath *)indexPath
{
CGSize maxSize = CGSizeMake(303, 200);
NSString* bodyText = [[self.data objectAtIndex:indexPath.row] objectForKey:kForumMessagesBody];
CGSize fitSize = [bodyText sizeWithFont:[UIFont systemFontOfSize:13] constrainedToSize:maxSize lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = 384 - 69 + fitSize.height;
NSLog(#"expandedHeight: %f",height);
return height;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Answer cell
if ([self.selectedCellIndexPath isEqual:indexPath])
{
cell = [tableView dequeueReusableCellWithIdentifier:[ForumCell expandedAnswerReuseIdentifier]];
if (cell == nil)
{
cell = [ForumCell expandedAnswerCell];
}
self.expandedCell = cell;
}
else
{
cell = [tableView dequeueReusableCellWithIdentifier:[ForumCell reqularAnswerReuseIdentifier]];
if (cell == nil)
{
cell = [ForumCell regularAnswerCell];
}
}
cell.labelMedia.text = [self.data objectAtIndex:indexPath.row];
return cell;
}
I also have custom cell, the class called ForumCell.h and ForumCell.m and it has 2 different XIB files: ForumRegularAnswerCell.xib and ForumExpandedAnswerCell.xib, I have the following code inside ForumCell.h:
+ (NSString*)reqularAnswerReuseIdentifier
{
return #"RegularAnswerCellReuseIdentifier";
}
+ (NSString*)expandedAnswerReuseIdentifier
{
return #"ExpandedAnswerCellReuseIdentifier";
}
+ (ForumCell*)regularAnswerCell
{
NSArray* objs = [[NSBundle mainBundle] loadNibNamed:#"ForumRegularAnswerCell" owner:self options:nil];
ForumCell* result = [objs objectAtIndex:0];
return result;
}
+ (ForumCell*)expandedAnswerCell
{
NSArray* objs = [[NSBundle mainBundle] loadNibNamed:#"ForumExpandedAnswerCell" owner:self options:nil];
ForumCell* result = [objs objectAtIndex:0];
return result;
}
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super initWithCoder:decoder];
if (self)
{
_originalCellHeight = self.frame.size.height;
_originalLblBodyHeight = self.lblBody.frame.size.height;
}
return self;
}
You can also use more than 2 xibs if you'd like its up to you. but this is the basics.
Enjoy!

How do you dynamically resize two text label widths inside of a UITableViewCell?

I'm using a table view that uses a custom UITableViewCell subclass, NoteCell. The subclass has two text labels, a name label and a date label which are side-by-side in the cell. My goal is to have the name label resize itself so that the full date is shown no matter what.
in cellForRowAtIndexPath, I try to calculate and set the widths of the two text views so that the date is fully displayed and the name label is truncated.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"NoteCell";
NoteCell *cell = (NoteCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
cell.dateLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;
cell.nameLabel.text = #"A name that should be truncated";
cell.dateLabel.text = #"A long date to use as an example";
// Set the widths
// First calculate how wide the date label needs to be.
cell.dateLabel.text = #"A really long date";
CGSize dateLabelSize = [cell.dateLabel.text sizeWithFont:cell.dateLabel.font];
CGFloat dateExpansionAmount = fabsf(dateLabelSize.width - cell.dateLabel.frame.size.width) + 10.0f;
cell.dateLabel.frame = CGRectMake(cell.dateLabel.frame.origin.x - dateExpansionAmount,
cell.dateLabel.frame.origin.y,
dateLabelSize.width,
cell.dateLabel.frame.size.height);
CGFloat nameLabelWidth = cell.dateLabel.frame.origin.x - cell.nameLabel.frame.origin.x;
cell.nameLabel.frame = CGRectMake(cell.nameLabel.frame.origin.x,
cell.nameLabel.frame.origin.y,
nameLabelWidth,
cell.nameLabel.frame.size.height);
}
Unfortunately, the results are not correct because I don't think I'm setting/calculating the frame bounds correctly. See the image below.
Disregarding the problems with the frame calculations is there a better way to approach this problem or is manually positioning the text label frame the only way?
try use this to set your dateLabel.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"NoteCell";
NoteCell *cell = (NoteCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
cell.dateLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;
cell.nameLabel.text = #"A name that should be truncated";
cell.dateLabel.text = #"A long date to use as an example";
//set frame cell.dateLabel
CGSize maximumSize = CGSizeMake(INT_MAX, 44); // CGSizeMake(width,height).
CGSize dateStringSize = [[cell.dateLabel.text sizeWithFont:[UIFont systemFontOfSize:cell.dateLabel.font]
constrainedToSize:maximumSize
lineBreakMode:UILineBreakModeWordWrap];
CGRect dateFrame = cell.dateLabel.frame;
dateFrame.size.width = dateStringSize. width;
cell.dateLabel.frame = dateFrame;
return cell;
}
after you set the text try to call
the method "sizeToFit"
and then you can just get the yourLabel.frame.size.width correctly, so if the 2 labels exceed the cell frame just resize the name label with a lower size and set correct position for the second, starting where the first one ends...
NEW EDIT (see comment)
cell.nameLabel.text = #"A name that should be truncated";
cell.dateLabel.text = #"A long date to use as an example";
[cell.nameLabel sizeToFit];
[cell.dateLabel sizeToFit];
// now check if the 2 width exceed cell size:
if (cell.nameLabel.frame.size.width + cell.dateLabel.frame.size.width > cell.frame.size.width){
//resize nameText:
cell.nameLabel.frame.size.width = cell.frame.size.width - cell.dateLabel.frame.size.width;
// ...and maybe consider to give it a minim size requirement
}
// now set the positions
cell.nameLabel.frame = CGRectMake(0,0,cell.nameLabel.frame.size.width,cell.nameLabel.frame.size.height );
cell.dateLabel.frame = CGRectMake(cell.nameLabel.frame.size.width,0,cell.dateLabel.frame.size.width,cell.dateLabel.frame.size.height );
ps
be sure your autoresizingMask settings are corrected to top and left correctly for both labels

Table cell last line being cutoff when resizing using NSString sizeWithFont:constrainedToSize:lineBreakMode:

When using the following code to re-size a table row the last line of text is always cutoff, no matter how many lines there are. But there is white space added that looks like enough space for the text.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
CGFloat restOfTheCellHeight = tableView.rowHeight - cell.detailTextLabel.frame.size.height;
CGSize constrainedSize = CGSizeMake(cell.detailTextLabel.frame.size.width, CGFLOAT_MAX);
CGSize textHeight = [cell.detailTextLabel.text sizeWithFont:cell.detailTextLabel.font constrainedToSize:constrainedSize lineBreakMode:cell.detailTextLabel.lineBreakMode];
CGFloat newCellHeight = (textHeight.height + restOfTheCellHeight);
if (tableView.rowHeight > newCellHeight) {
newCellHeight = tableView.rowHeight;
}
return newCellHeight;
}
Here is the code in cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCellTableRowTypeSingleLineValueSmallLabel *cell = (CustomCellTableRowTypeSingleLineValueSmallLabel *)[tableView dequeueReusableCellWithIdentifier:#"CellTypeMultiLineLabelInCellSmallCell"];
if (cell == nil) {
NSArray *xibObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCellTableRowTypeSingleLine" owner:nil options:nil];
for(id currentObject in xibObjects) {
if([currentObject isKindOfClass:[CustomCellTableRowTypeSingleLineValueSmallLabel class]]){
cell = (CustomCellTableRowTypeSingleLineValueSmallLabel *)currentObject;
}
}
cell.accessoryType = UITableViewCellAccessoryNone;
cell.editingAccessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.detailTextLabel.numberOfLines = 0;
cell.detailTextLabel.text = self.attributeStringValue;
cell.textLabel.text = self.rowLabel;
return cell;
}
Any ideas?
You need to call [cell.detailTextLabel sizeToFit] in order for the label to actually resize in cellForRowAtIndexPath. It will not resize on its own just because you set numberOfLines to 0. See this question and read its answers for more clarification.
You are calculating the cell height appropriately in your heightForRowAtIndexPAth method, but then in your cellForRowAtIndexPath method you are never actually using it to set the height of your label within it.
So the table is allocating the right amount of space based on your heightForRowAtIndexPath, but then inserting into that space the unresized cell that you return from cellForRowAtIndexPath. I think this might the the cause of the problem and would explain the results you are seeing.
In cellForRowAtIndexPath you need to actually set the height of the label using the same calculation.
i.e.
CGSize constrainedSize = CGSizeMake(cell.detailTextLabel.frame.size.width, CGFLOAT_MAX);
CGRect cframe = cell.detailTextLabel.frame;
cframe.size.height = constrainedSize.height;
cell.detailTextLabel.frame = cframe;
You may also need to actually set the content view frame as well (not sure how it works with a non-custom cell).
I'm also not sure its a good idea to be calling cellForRowAtIndexPath from the heightForRowAtIndexPath method (it would probably be better to just directly access the text data you are using for the size calculation directly).
Turns out I just needed to enable all of the Autosizing options in interface builder for the label.

How to show the sum of values as a "Title" for each section in a grouped UITableView?

Each section in my table consists of one or more cells that contain a number. I would like to show the sum of the numbers as part of each section title.
For example:
section title: The following sum is: 32
3
5
7
8
9
section title: The following sum is: 20
2
4
6
8
My question is, "where do I perform the tally for each section and how do I access just the cells from each section? i.e. which delegate function, etc... so that the tally is ready for me to display here in this routine?
Thank you.
Here is my current TitleForHeaderInSection delegate function.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo name];
}
UPDATE:
Here is the code that pulls each cell's content from the fetchedresultscontroller and places it in the cell.
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Grade *sGrade = [_fetchedResultsController objectAtIndexPath:indexPath];
float averageScore, _score, _total;
_score = [sGrade.scoreValue floatValue];
_total = [sGrade.scorePossible floatValue];
averageScore = (_score / _total) * 100;
cell.textLabel.text = [NSString stringWithFormat: #"%2.2f%% - (%# of %#)", averageScore, [sGrade.scoreValue stringValue], [sGrade.scorePossible stringValue]];
cell.detailTextLabel.text = [sGrade commentInfo];
}
So in reality, each of my cells look something like this: 87.00% - (87 / 100). And so if I have 8 quiz scores like this in the "quizzes" section, then I want to show the average score for all quizzes as part of the title for that section in the grouped UITable.
You can use tableView:viewForHeaderInSection: Method and add a custom view in header with a label in it. The label will display the sum.
I am sure this would work. I have used this in many of my apps.
Code:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *myHeader = [[UIView alloc] initWithFrame:CGRectMake(0,60,320,20)];
myHeader.backgroundColor = [UIColor blueColor];
float totpoints = //This would include the summation logic for the section;
NSString *Sum = [NSString stringWithFormat:#"%.02f",totpoints];
UILabel *myLabel1 = [[UILabel alloc] initWithFrame:CGRectMake(0,0,80,20)] ;
myLabel1.font = [UIFont boldSystemFontOfSize:14.0];
myLabel1.textColor = [UIColor whiteColor];
myLabel1.backgroundColor = [UIColor blueColor];
myLabel1.text = Sum;
myLabel1.textAlignment = UITextAlignmentCenter;
[myHeader addSubview:myLabel1];
return myHeader;
}
I would better go for a dictionary with section number as keys and arrays as objects and then simply I have to sum up the array values fetched based on section number.
Hope this helps.
EDIT:
Also using tableView:viewForHeaderInSection: is beneficial because we can put the section header in any position we want as far as right,left and center alignment of the label "myLabel" on the custom View "myHeader" is concerned.
And putting proper background color and other minor but visually important changes can be made using tableView:viewForHeaderInSection:
Something like this (untested, but close)
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
float averageScoreTotal, averageScoreCount, _score, _total;
for (Grade *sGrade in sectionInfo.objects)
{
_score = [sGrade.scoreValue floatValue];
_total = [sGrade.scorePossible floatValue];
averageScoreTotal += (_score / _total) * 100;
averageScoreCount++;
}
return [NSString stringWithFormat: #"The following sum is: %2.2f%%", averageScoreTotal / averageScoreCount];
}
This would be the quickest way:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
NSNumber *avg=[[sectionInfo objects] valueForKey:#"#avg.scoreValue"];
NSString *title=[[sectionInfo name] stringByAppendingFormat:#":The following sum is:%#",avg];
return title;
}