I'm confused as to what the problem/difference is with this code. In the first working example, I'm loading the table view from a custom class. This works dandy.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"EventCell";
ISEventsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithWhite:.2 alpha:.7];
ISSingleEvent *singleEvent = events[indexPath.row];
[cell populateWithEvents:(singleEvent)];
return cell;
}
Now I want to add a second section to the table view and everything seems to be going fine until I add the conditions for cellForRowAtIndexPath. I get the error "No visible interface for 'UITableViewCell' declares the selector 'populateWithPasses:'
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier1 = #"EventCell";
static NSString *CellIdentifier2 = #"FlareCell";
NSString *identityString = #"";
switch ([indexPath section]) {
case 0: {
identityString = CellIdentifier1;
break;
}
case 1: {
identityString = CellIdentifier2;
break;
}
default:
break;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identityString];
if ([indexPath section] == 0) {
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2
reuseIdentifier:CellIdentifier1];
cell.backgroundColor = [UIColor colorWithWhite:.2 alpha:.7];
ISSingleEvent *singleEvent = events[indexPath.row];
// Error here (below) "No visible interface for 'UITableViewCell' declares the selector 'populateWithEvents:'
[cell populateWithEvents:(singleEvent)];
}
} else {
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier2];
cell.backgroundColor = [UIColor colorWithWhite:.2 alpha:.7];
ISFlareEvent *singleEvent = flareEvents[indexPath.row];
// Error here (below) "No visible interface for 'UITableViewCell' declares the selector 'populateWithFlares:'"
[cell populateWithFlares:(singleEvent)];
}
}
return cell;
}
So what am I doing wrong here? When I called "populateWithEvents" with only one section, it didn't throw an error. Now I've set up two section, each with it's own tableviewcell and it's just not working. Any help would be greatly appreciated.
Thanks,
just cast your cell like this
[(ISEventsCell *)cell populateWithFlares:(singleEvent)];
You are trying to call populateWithFlares: for UITableviewCell. The metho is defined for ISEventsCell
Related
My TableView has three different sections. Section 0 is for photo uploads its virtually empty until a user uploads a photo. However, my tableview is reusing cells from section 1 in section 2 and i'm not sure why. Here is the code below. Any help is greatly appreciated.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"cell";
if (indexPath.section == 0) {
UploadsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UploadsCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.tag = indexPath.row;
cell.delegate = self;
cell.imageInfo = [_uploads objectAtIndex:indexPath.row];
[cell setCellInfo];
cell.backgroundColor = [UIColor redColor];
return cell;
}
else{
id object;
if (indexPath.section == 1) {
object = [self cacheObjectAtIndexPath:indexPath];
return [self tableView:tableView cellForRowAtIndexPath:indexPath withObject:object];
}
if (indexPath.section == 2) {
object = [self objectAtIndexPath:indexPath];
}
return [self tableView:tableView cellForRowAtIndexPath:indexPath withObject:object];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath withMobiObject:(MobiObject *)object{
static NSString *CellIdentifier = #"Cell";
StylesCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = (StylesCell*)[[StylesCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (object) {
[cell setObject:object];
}
return cell;
}
You should first begin by making your methods a bit more readable. Since you've asked me for it, I've rewritten one of your larger methods to use a switch when returning the correct configuration for cells based on their section.
As for your actual problem, it's very likely associated with the following lines returning the configuration for Section 1:
id object = [self cacheObjectAtIndexPath:indexPath];
return [self tableView:tableView cellForRowAtIndexPath:indexPath withObject:object];
As well as the other line returning configuration for Section 2:
id object = [self objectAtIndexPath:indexPath];
return [self tableView:tableView cellForRowAtIndexPath:indexPath withObject:object];
I'm not sure exactly what these methods return, so it's hard to say what exactly about them is returning duplicate cells.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section) {
case 0:
// Section is Zero
UploadsCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
cell.tag = indexPath.row;
cell.delegate = self;
cell.imageInfo = [_uploads objectAtIndex:indexPath.row];
[cell setCellInfo];
[cell setBackgroundColor:[UIColor redColor]];
return cell;
case 1:
// Section is One
id object = [self cacheObjectAtIndexPath:indexPath];
return [self tableView:tableView cellForRowAtIndexPath:indexPath withObject:object];
case 2:
// Section is Two
id object = [self objectAtIndexPath:indexPath];
return [self tableView:tableView cellForRowAtIndexPath:indexPath withObject:object];
default:
// Section is neither of the aforementioned sections.
break;
}
}
Here's my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifierNormal = #"CellNormal";
static NSString *CellIdentifierTracking = #"CellTracking";
switch (self.mapState) {
case MapStateNormal:
{
// UITableViewCell *cell = [self.table dequeueReusableCellWithIdentifier:CellIdentifierNormal];
// if (cell == nil) {
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifierNormal];
// }
[cell.textLabel setText:#"abc"];
return cell;
}
case MapStateTracking:
{
// UITableViewCell *cell = [self.table dequeueReusableCellWithIdentifier:CellIdentifierTracking];
// if (cell == nil) {
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifierTracking];
// }
if (indexPath.row == 0) {
[cell.textLabel setText:#"Lorem ipsum"];
} else {
NSURL *url = [LoremIpsum URLForPlaceholderImageFromService:LoremIpsumPlaceholderImageServicePlaceKittenCom
withWidth:1024
height:1024];
[cell.imageView setImageWithURL:url
placeholderImage:[UIImage imageNamed:#"MenuButton"]];
}
return cell;
}
default:
break;
}
return nil;
}
This piece of code works fine but not the best practice because I re-create UITableViewCell everytime. It display like this:
However, when I uncomment those lines above to enable dequeueReusableCell then the table shows its cells with errors like this (the yellow part is my code):
You can see that there's an UIImage in first row and text in some rows below while I clearly didn't set it in my code.
What could I do to fix this ? or should I stick with the first method ?
Thanks.
You should really re-use the table view cells, because it is a lot of overhead if you recreate them all the time, i.e. the out-commented code is right.
Next, the docs say: "The table view's delegate in tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell."
If you do not reset the content, it will be shown again.
So I suggest that you set
cell.imageView.image = nil;
in your -(UITableViewCell *)tableView:cellForRowAtIndexPath:(NSIndexPath *)indexPath method.
Simply set [cell.textLabel setText:#""]; for each cell you dont't want to display any text. Cells are reused with the previous text, so you need to clear it.
Try this, clear the cell if it cell != nil
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifierNormal = #"CellNormal";
static NSString *CellIdentifierTracking = #"CellTracking";
switch (self.mapState) {
case MapStateNormal:
{
UITableViewCell *cell = [self.table dequeueReusableCellWithIdentifier:CellIdentifierNormal];
if (cell == nil) {
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifierNormal];
} else {
[cell.textLabel setText:#""];
[cell.imageView setImage:nil];
}
...
return cell;
}
case MapStateTracking:
{
UITableViewCell *cell = [self.table dequeueReusableCellWithIdentifier:CellIdentifierTracking];
if (cell == nil) {
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifierTracking];
} else {
[cell.textLabel setText:#""];
[cell.imageView setImage:nil];
}
....
return cell;
}
default:
break;
}
return nil;
}
I have two custom cells. And I want to display 2 sections in my UITableView. The first section with one row displaying the first custom cell, and the second section displaying a list of objects pulled from core data.
How should I implement the "cellForRowAtIndexpath" method ?
Here is some of my code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (section == 0) {
return 1;
} else if (section == 1) {
//gastos is an array
return [self.gastos count];
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section) {
case 0:
{
SaldoCelda *cell1 = [tableView dequeueReusableCellWithIdentifier:#"Cell1" forIndexPath:indexPath];
return cell1;
}
case 1:
{
static NSString *CellIdentifier = #"Cell";
CeldaGasto *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSManagedObject *gasto = [self.gastos objectAtIndex:indexPath.row];
[cell.monto setText:[NSString stringWithFormat:#"%# AR$", [gasto valueForKey:#"monto"]]];
[cell.categoria setText:[NSString stringWithFormat:#"%#", [gasto valueForKey:#"categoria"]]];
[cell.fecha setText:[NSString stringWithFormat:#"%#", [gasto valueForKey:#"fecha"]]];
return cell;
}
default:
break;
}
return 0;
}
And this is the error message I get:
Assertion failure in -[UITableView _configureCellForDisplay:forIndexPath:], /SourceCache/UIKit_Sim/UIKit-2903.23/UITableView.m:6246
2014-03-05 01:02:57.181 Spendings[2897:70b] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
Thankyou for your help!
I have made a test and it works fine. These are the steps:
Create UITableViewController in storyBoard
Drag and drop UITableViewCell on a UITableViewController below the cell that is already there
Assign CellIdentifier to both cells (I used Cell1 and Cell2)
Create 2 subclasses of UITableViewCell (I called them Cell1 and Cell2)
Create subclass of UITableViewController and name it somehow
in cellForRowAtIndexPath method:
static NSString *CellIdentifier = #"Cell1";
static NSString *CellIdentifier1 = #"Cell2";
switch (indexPath.section) {
case 0:
{
Cell1 *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
return cell;
}
break;
case 1:
{
Cell2 *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1 forIndexPath:indexPath];
return cell;
}
break;
default:
break;
}
return nil;
As you can see the implementation is the same as yours
The only way I could reproduce your error is by returning nil in switch block and the documentation for dequeueReusableCellWithIdentifier says:
This method always returns a valid cell.
Even if you messed up your cell identifiers, you still wouldn't get the error you posted. So my conclusion is:
reboot, clean project, restart simulator or something like that cause your scenario according to documentation is not possible...
Beside you are returning nil for one of your cells, you also has an issue in numberOfRowsInSection, you are returning 0 rows for section 1!!, although you are expecting rows in this section in your cellForRowAtIndexPath.
you can create set of cell in one xib and fetch it in cellForRowAtindex with it's identifier. Now you can set it's property and data to it.
static NSString *CellIdentifier = #"MyCellIdentifier";
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
cell = (MyCell *)[nib objectAtIndex:0];
cell1=(MyCell *)[nib objectAtIndex:1];
}
Try This i'm modifying your code
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (section == 0) {
return 1;
}
else if (section == 2) {
//gastos is an array
return [self.gastos count];
}
//return 0; you should not write this because at the end of execution of this method it will return 0
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
switch (indexPath.section) {
case 0:
{
SaldoCelda *cell1 = [tableView dequeueReusableCellWithIdentifier:#"Cell1" forIndexPath:indexPath];
cell=cell1;
break;
}
case 1:
{
static NSString *CellIdentifier = #"Cell";
CeldaGasto *cell2 = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSManagedObject *gasto = [self.gastos objectAtIndex:indexPath.row];
[cell.monto setText:[NSString stringWithFormat:#"%# AR$", [gasto valueForKey:#"monto"]]];
[cell.categoria setText:[NSString stringWithFormat:#"%#", [gasto valueForKey:#"categoria"]]];
[cell.fecha setText:[NSString stringWithFormat:#"%#", [gasto valueForKey:#"fecha"]]];
cell=cell2;
break;
}
default:
break;
}
return cell;
//return 0; remove this in your code
}
Hope this will help
I am using tableview apple's lazy loading code in my project,but having exception in project. And error is - *** Assertion failure in -[UITableView _configureCellForDisplay:forIndexPath:],here is my code please help.But it is working in other project.I have no delegate method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"LazyTableCell";
static NSString *PlaceholderCellIdentifier = #"PlaceholderCell";
NSUInteger nodeCount = [self.entries count];
if (nodeCount == 0 && indexPath.row == 0)
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier];
cell.detailTextLabel.text = #"Loading…";
return cell;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (nodeCount > 0)
{
AppRecord *appRecord = [self.entries objectAtIndex:indexPath.row];
cell.textLabel.text = appRecord.name;
if (!appRecord.appIcon)
{
if (self.mytable_view.dragging == NO && self.mytable_view.decelerating == NO)
{
[self startIconDownload:appRecord forIndexPath:indexPath];
}
cell.imageView.image = [UIImage imageNamed:#"Placeholder.png"];
}
else
{
cell.imageView.image = appRecord.appIcon;
}
}
return cell;
}
Because cellForRowAtIndexPath is returning a nil value and then configureCellForDisplay is expecting a UITableViewCell. The storyboard or XIB does not have a cell defined with the cell identifier you specified. Better check the spelling of identifier.
Please check for Delegate.
Have you added both delegate or not?
UITableViewDelegate, UITableViewDataSource
import UIKit
class UserFriendVC: UIViewController, UITableViewDelegate, UITableViewDataSource
{
override func viewDidLoad() {
super.viewDidLoad()
}
}
you should add cellIdentifier from "Show the attributes inspector". Please fallow just like below snippet.
Firstly added below code to your-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath delagate method.
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
ResultCustomTableViewCell *cell = (ResultCustomTableViewCell *)[resultTableView dequeueReusableCellWithIdentifier:#"ResultTableViewCellIdentifier"];
...
//set the cell property
...
return cell;
}
and then jump the "Show the attributes inspector", While selected cell root view.
And then paste the same identifier name to identifier section in "Show the attributes inspector". in this case i use this ResultTableViewCellIdentifier.
And one more think. if you use custom cell just like this scenario, you must add below delegate metod. Because your custom cell height can be higher or smaller original tableview height. And Also you should register this nib in viewDidLoad.
- (void)viewDidLoad {
[resultTableView registerNib:[UINib nibWithNibName:#"ResultCustomTableViewCell" bundle:nil] forCellReuseIdentifier:[ResultCustomTableViewCell reuseIdentifier]];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGFloat height = 0.0;
if (tableView == resultTableView){
height = 44.0f;
}
return height;
}
i hope, it'll fix your problem
CellIdentifier I bet your cellForRowAtIndexPath is returning nil.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"LazyTableCell";
static NSString *PlaceholderCellIdentifier = #"PlaceholderCell";
NSUInteger nodeCount = [self.entries count];
if (nodeCount == 0 && indexPath.row == 0) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier];
if(cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:PlaceholderCellIdentifier] autorelease];
}
cell.detailTextLabel.text = #"Loading…";
return cell;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
if (nodeCount > 0) {
AppRecord *appRecord = [self.entries objectAtIndex:indexPath.row];
cell.textLabel.text = appRecord.name;
if (!appRecord.appIcon)
{
if (self.mytable_view.dragging == NO && self.mytable_view.decelerating == NO)
{
[self startIconDownload:appRecord forIndexPath:indexPath];
}
cell.imageView.image = [UIImage imageNamed:#"Placeholder.png"];
}
else
{
cell.imageView.image = appRecord.appIcon;
}
}
return cell;
}
That's probably why [UITableView _configureCellForDisplay:forIndexPath:] is failing... because cellForRowAtIndexPath is returning a null value and then configureCellForDisplay is expecting a UITableViewCell.
Register your cell in the - (void)viewDidLoad method using
[self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:#"Your Reuse Identifier"];
if your are using storyboards and use this if you are using a custom cell.
[self.tableView registerNib:UINib nibWithNibName:#"Your File Name.xib" bundle:nil forCellReuseIdentifier:#"Your Reuse Identifier"]
I like to add multiple reusable UITableViewCells to a TableView. I am using this code to do this, but it won't work, it only shows the first cell.
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
static NSString *costumeCell1 = #"Cell1";
AppDetailCell1 *cell1 = [tableView dequeueReusableCellWithIdentifier:costumeCell1];
if (!cell1) {
cell1 = [[AppDetailCell1 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:costumeCell1];
}
return cell1;
}
if (indexPath.row == 1) {
static NSString *costumeCell2 = #"Cell2";
AppDetailCell2 *cell2 = [tableView dequeueReusableCellWithIdentifier:costumeCell2];
if (!cell2) {
cell2 = [[AppDetailCell2 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:costumeCell2];
}
return cell2;
} else {
return nil;
}
}
If you set the reuse identifiers properly, you should change this code part
AppDetailCell1 *cell1 = [tableView dequeueReusableCellWithIdentifier:costumeCell1];
if (!cell1) {
cell1 = [[AppDetailCell1 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:costumeCell1];
}
with this
AppDetailCell1 *cell1 = [tableView dequeueReusableCellWithIdentifier:costumeCell1 forIndexPath:indexPath];
There is no need if(!cell1), because dequeueReusableCellWithIdentifier: forIndexPath: method never returns nil, as I said before that properly reuse identifier set is important.