Reputation: 115
I am trying to create a table view which uses two types of cells - default and subtitle.
I tried to use a single reusable cell (*cell
) which seemed to work fine until I got to the bottom cell which was off screen - when this came into view it was a duplicate of the first visible cell.
I thought that I could try to add a second type of cell (*cellB
) and when I did it seemed to solve the problem however it would crash every so often with the following error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
I know that I am doing something wrong and I am pretty sure I am not implementing the reusable cells properly but after several days of frustration I would really appreciate some advice.
P.S. I have searched many previous posts but none seem to cover the issue of using two types of cells on the same table.
Thanks in advance.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
UITableViewCell *cellB = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCellB"];
switch (indexPath.section) {
case 0:
if (cell == nil) {
// The only subtitle cell
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:@"UITableViewCell"];
}
[[cell textLabel] setText:title];
[[cell textLabel] setTextColor:[UIColor whiteColor]];
[[cell detailTextLabel] setText:[NSString stringWithFormat:@"Entry: £%@",price]];
[[cell detailTextLabel] setTextColor:[UIColor colorWithWhite:1.0 alpha:.8]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
break;
case 1:
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"UITableViewCell"];
UIView *cellBackView = [[UIView alloc] initWithFrame:CGRectZero];
cellBackView.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:@"PhotoFrame.png"]];
cell.backgroundView = cellBackView;
}
[lImage setFrame:CGRectMake(0, 23, 320, 200)];
[cell.contentView addSubview:lImage];
break;
case 2:
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"UITableViewCell"];
UIView *cellBackView = [[UIView alloc] initWithFrame:CGRectZero];
cellBackView.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:@"PaperTop.png"]];
cell.backgroundView = cellBackView;
}
break;
case 3:
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"UITableViewCell"];
UIView *cellBackView = [[UIView alloc] initWithFrame:CGRectZero];
cellBackView.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:@"Paper.png"]];
cell.backgroundView = cellBackView;
}
break;
case 4:
// I'm pretty sure this bit is wrong but if I use (cell == nil) the first cell is shown instead
if (cellB == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"UITableViewCellB"];
UIView *cellBackView = [[UIView alloc] initWithFrame:CGRectZero];
cellBackView.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:@"PaperBottom.png"]];
cell.backgroundView = cellBackView;
}
break;
default:
break;
}
return cell;
}
Upvotes: 1
Views: 2481
Reputation: 5960
This method is called once for each cell that needs to be filled. If you scroll the table, rows will go off the screen and be queued with their reuse identifier. When rows scroll onto the table, this method is called to fill the cells as they become visible.
So first, you only want to dequeue one cell each time this method is called. By dequeuing cells of type UITableViewCell' and
UITableViewCellB' you are dequeuing one that isn't going to be used. So you need to determine which type of cell you need before you dequeue one, and then dequeue the right type (by its reuse identifier).
Second, the purpose of the cell queuing mechanism is so you don't have to do things like customize the cell appearance every time it appears in the view. If a cell with that type of appearance is already in the queue, then it should come out of the queue already set up, and you only need to put the data into it. This is done for performance (speed), but it may not make a lot of difference in your case.
I may be wrong about this, and I'll correct my answer if I am, but the error message may be because the number of sections returned by numberOfSections
and/or the number or rows returned by numberOfRowsInSection:
is not correct and doesn't match the data source. It is trying to access an element of the data source that doesn't exist.
What kind of data source are you using, and can you show the code for numberOfSections
and numberOfRowsInSection:
?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"UITableViewCell"];
UIView *cellBackView; // move this outside of the switch block
switch (indexPath.section) {
case 0:
[[cell textLabel] setText:title];
[[cell textLabel] setTextColor:[UIColor whiteColor]];
[[cell detailTextLabel] setText:[NSString stringWithFormat:@"Entry: £%@",price]];
[[cell detailTextLabel] setTextColor:[UIColor colorWithWhite:1.0 alpha:.8]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
break;
case 1:
cellBackView = [[UIView alloc] initWithFrame:CGRectZero];
cellBackView.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:@"PhotoFrame.png"]];
cell.backgroundView = cellBackView;
[lImage setFrame:CGRectMake(0, 23, 320, 200)];
[cell.contentView addSubview:lImage];
break;
case 2:
cellBackView = [[UIView alloc] initWithFrame:CGRectZero];
cellBackView.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:@"PaperTop.png"]];
cell.backgroundView = cellBackView;
break;
case 3:
cellBackView = [[UIView alloc] initWithFrame:CGRectZero];
cellBackView.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:@"Paper.png"]];
cell.backgroundView = cellBackView;
break;
case 4:
cellBackView = [[UIView alloc] initWithFrame:CGRectZero];
cellBackView.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:@"PaperBottom.png"]];
cell.backgroundView = cellBackView;
break;
default:
break;
}
return cell;
}
Upvotes: 2
Reputation: 64002
As cgull pointed out, when [indexPath section]
is 4
, you're initializing cellB
, but still returning cell
, which may or may not have a valid cell in it.
You need to have one variable that holds the cell you're going to return, no matter the path taken through the switch
:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString identifier = @"UITableViewCell";
if( [indexPath section] == 4 ){
identifier = @"UITableViewCellB";
}
// Try to dequeue the appropriate cell kind
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
switch (indexPath.section) {
//other cases...
case 4:
// You now have only one variable that can possibly hold a cell.
// If section is 4, it's either nil or a UITableViewCellB kind.
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"UITableViewCellB"];
// Set up new cell
}
break;
default:
break;
}
return cell;
}
Upvotes: 0
Reputation: 1417
If cellB
isn't null, you're still returning cell
and not cellB
.
Edit:
To fix, add code in the case 4
block as follows:
case 4:
if (cellB == nil) {
// No changes in here
}
else { //
cell = cellB; // Add these 3 lines
} //
break;
Upvotes: 2
Reputation: 4805
cgull is correct. Replace case 4 with this:
case 4:
if (cellB == nil) {
// assign to the correct cell
cellB = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"UITableViewCellB"];
UIView *cellBackView = [[UIView alloc] initWithFrame:CGRectZero];
cellBackView.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:@"PaperBottom.png"]];
cell.backgroundView = cellBackView;
}
return cellB; // <--- return the correct cell
Upvotes: 0