Gold Challenge Question


#1

I am trying to get this to work for the Bronze Challenge case where there may be 1 or 2 sections. In tableView:heightForRowAtIndexPath: I get a runtime error (EXC_BAD_ACCESS) on [tableView numberOfRowsInSection:secNum]. I want to compare this to the [indexPath row] of the last section displayed to determine how to set the row height.

My ItemsViewController.m code is shown below.

Any suggestions on what I am doing wrong?

Thanks,

Rick

[code]#import “ItemsViewController.h”
#import “BNRItemStore.h”
#import “BNRItem.h”

@implementation ItemsViewController

-(id)init
{
// Call the superclass’s designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
for (int i = 0; i < 5; i++) {
[[BNRItemStore sharedStore] createItem];
}
}
return self;
}

-(id)initWithStyle:(UITableViewStyle)style
{
return [self init];
}

  • (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    CGFloat height;
    NSInteger rowNum = [indexPath row];
    NSInteger secNum = [indexPath section];
    NSInteger nSecs = [tableView numberOfSections];
    NSInteger nRows = [tableView numberOfRowsInSection:secNum];

    if (nSecs == secNum+1 & nRows == rowNum+1 ) height = 44.0;
    else height = 60.0;

    return height;
    }

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Sets number of sections to 1 or 2 based on count of each array
int i = 0;
if ([[[BNRItemStore sharedStore] gt50Items] count] > 0) i++;
if ([[[BNRItemStore sharedStore] lte50Items] count] >0) i++;
return i;

}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int gt50Count = [[[BNRItemStore sharedStore] gt50Items] count];
int lte50Count = [[[BNRItemStore sharedStore] lte50Items] count];
int extraRow = 0;
if ([tableView numberOfSections] == 1) extraRow = 1;
if (section == 0) {
if (gt50Count > 0) return gt50Count + extraRow;
else return lte50Count + extraRow;
}
else return lte50Count + 1;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Check for a reusable cell first, use that if it exists
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@“UITableViewCell”];

// If there is no reusable cell of this type, create a new one
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"];
}
    
// Set the text on the cell with the description of the item
// that is at the nth index of items, where n = row this cell
// will appear is on the tableview
int gt50Count = [[[BNRItemStore sharedStore] gt50Items] count];
int lte50Count = [[[BNRItemStore sharedStore] lte50Items] count];        

if ([indexPath section] == 0) {
    if (gt50Count > 0 & [indexPath row] < gt50Count) {
        BNRItem *p = [[[BNRItemStore sharedStore] gt50Items] objectAtIndex:[indexPath row]];
        [[cell textLabel] setText:[p description]];            
    }
    else if (lte50Count > 0 & [indexPath row] < lte50Count) {
        BNRItem *p = [[[BNRItemStore sharedStore] lte50Items] objectAtIndex:[indexPath row]];
        [[cell textLabel] setText:[p description]];
    }
    else if ([tableView numberOfSections] == 1) [[cell textLabel] setText:@"No more items!"];
}
else if ([indexPath section] == 1) {
    if (lte50Count > 0 & [indexPath row] < lte50Count) {
        BNRItem *p = [[[BNRItemStore sharedStore] lte50Items] objectAtIndex:[indexPath row]];
        [[cell textLabel] setText:[p description]];            
    }
    else [[cell textLabel] setText:@"No more items!"];
}
return cell;

}

@end
[/code]


#2

Hi Rick

In your heightForRowAtIndexPath method, your line to compare section and row values is

if (nSecs == secNum+1 & nRows == rowNum+1 ) height = 44.0; else height = 60.0;
Try with a double &&

if (nSecs == secNum + 1 && nRows == rowNum + 1) height = 44.0; else height = 60.0;


#3

Hi,

I did catch that and correct it but it still doesn’t work. The error occurs before the if statement when I use the tableView:numberOfRowsInSection: method. However, I did find that if I put the code from tableView:numberOfRowsInSection: (modified to compute nRows) directly into tableView:heightForRowAtIndexPath: then it works.

[code]- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height;
NSInteger rowNum = [indexPath row];
NSInteger secNum = [indexPath section];
NSInteger nSecs = [tableView numberOfSections];
NSInteger nRows;

// Put in code directly from numberOfRowsInSection to get directly from data source
int gt50Count = [[[BNRItemStore sharedStore] gt50Items] count];
int lte50Count = [[[BNRItemStore sharedStore] lte50Items] count];
int extraRow = 0;
if (nSecs == 1) extraRow = 1;
if (secNum == 0) {
    if (gt50Count > 0) nRows = gt50Count + extraRow;
    else nRows = lte50Count + extraRow;
}
else nRows = lte50Count + 1;
   
if (nSecs == secNum+1 && nRows == rowNum+1 ) height = 44.0;    
else height =  60.0;

return height;

}
[/code]


#4

@rickjow

It seems to be the line NSInteger nRows = [tableView numberOfRowsInSection:secNum];that causes an indefinite loop in the numberOfRowsInSection method.

In your case, I resolved it by taking out the nRows line calling the numberOfRowsInSection method, and then comparing the number of items in the last section (2nd in this case) in the if statement (note also changed rowNum+1 to rowNum within the if:

[code]- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height;
NSInteger rowNum = [indexPath row];
NSInteger secNum = [indexPath section];
NSInteger nSecs = [tableView numberOfSections];

//NSInteger nRows = [tableView numberOfRowsInSection:secNum];

// My last section is for the cheaper items (in your case lte50Items)
NSInteger numberItemsInLastSection = [[[BNRItemStore sharedStore] lte50Items] count];

if (nSecs == secNum+1 & numberItemsInLastSection == rowNum ) height = 44.0;    
else height =  60.0;

return height;

}[/code]
This works but relies on hard-coding the call to the store for the last section row count, so unfortunately have to change this line if the number of sections change.

Mark


#5

Mark,

Thanks for the help. I have been doing more hard-coding than I would like on some of the challenges. Hopefully, I can come up with more ‘elegant’ solutions as I get a better grasp of the concepts.

Rick


#6

Why don’t you guys simplify?

  • (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {

//theRest is an Array with objects lesser than 50 (valueInDollar)

if (!indexPath.section == 1 || indexPath.row < [theRest count] -1) {
    return 60.f;
}else{
    return 44.f;
}

}

It is hard code but simpler. You could reduce hard coding by creating some properties.
Cheers


#7

Actually, I think this solution works good too:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NSArray *sectionArray = [tableData objectAtIndex:[indexPath section]]; if ([indexPath row] < [sectionArray count]) return 60.0; return 44.0; }

BTW, this is my first post, here in the forums :wink:

forgot to mention that this solution is based on sjk1000’s solution (bronze challenge) where tableData is essentially an array of arrays (one for over and one for under $50).

and it works thanks to:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [[tableData objectAtIndex:section] count] + 1; //+1 for "No more items" }