I have a custom cell with multiple cell Identifiers. I tried the following code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 2;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellID;
switch ([indexPath section]) {
case 0:
cellID = #"firstCell";
break;
case 1:
cellID = #"secondCell";
break;
default:
break;
}
customCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];
return cell;
}
When I run the app, it displays the first cell 2 times, even though in the storyboard, there are 2 cells with different objects inside.
Is [indexPath section] really what you want?
I think you want [indexPath row]
Related
I have a UIViewController with a UITableView in it. I have 3 sections for the UITableView, like this.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3;
}
and my cells set up like this...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ContactInfoCellIdentifier";
NSUInteger section = [indexPath section];
UITableViewCell *cell;
switch (section)
{
case 0:
{
...
break;
}
case 1:
{
...
break;
}
case 2:
{
...
break;
}
default:
break;
}
return cell;
}
but for some reason case 2, section 3 never gets called. Any ideas why this might be?
You need to instantiate the UITableViewCell like this UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:"YourIdentifier"];
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 had a simple question regarding a table view with 3 different kinds of prototype cells. The first two occur just once while the third occurs 4 times. Now what I'm confused about is how to specify in my cellforRowatindexpath which cell prototype to use for which row. So, I want something like for row 0, use prototype 1, for row 1, use prototype 2, for rows 3,4,5 and 6 use prototype 3. What's the best way to do this? Do i give each prototype an identifier and then use dequeueReusableCellWithIdentifier:CellIdentifier ?
Can you'll provide some sample code?
EDIT:
Still not working. This is the code I have at the moment. ( I only have one case for the switch statment because I just want to test and see if the cell is being generated in the first row or not, but currently table view is blank)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch(indexPath.row)
{
case 0: {static NSString *CellIdentifier = #"ACell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:#"ACell"];
if(cell==nil) {
cell=[[UITableViewCell alloc]
initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:#"ACell"];
}
return cell;
break;
}
}
}
Acell is my identifier for a cell prototype that I created. I
If you are using three prototype then use three identifiers. Because only one identifier will cause problem. And you will get wrong result. So code like this.
if(indexPath.row==0){
// Create first cell
}
if(indexPath.row==1){
// Create second cell
}
else{
// Create all others
}
You can use switch case also here for best performance.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell.tag == 0)
{
return array1.count;
}
else(cell.tag == 1)
{
return array2.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier;
NSString *membershipType = [membershipTypeArray objectAtIndex:indexPath.row];
if ([membershipType isEqualToString:#"silver"]||[membershipType isEqualToString:#"gold"])
{
cellIdentifier = #"cell";
}
else if ([membershipType isEqualToString:#"platinum"])
{
cellIdentifier = #"premiumCustomCell";
cell.customCellImageView.image = [cellImageArray objectAtIndex:indexPath.row];
}
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[CustomCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.headingLabel.text = [titleArray objectAtIndex:indexPath.row];
}
Here i wrote code like:
#pragma mark == Tableview Datasource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger nRows = 0;
switch (section) {
case 0:
nRows = shipData.count;
break;
case 1:
nRows = dataArray1.count;
break;
default:
break;
}
return nRows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellIdentifier = #"cellIdentifier1";
NSString *cellIdentifier1 = #"cellIdentifier2";
SingleShippingDetailsCell *cell;
switch (indexPath.section) {
case 0:
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
//Load data in this prototype cell
break;
case 1:
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier1];
//Load data in this prototype cell
break;
default:
break;
}
return cell;
}
I have a tableview with multiple prototype cells that i designed in the storyboard, but i´m stuck with the height problem because my first cell is supose to be different from the second one and so on...I have different identifiers for each cell, and because i designed them in storyboard, i know they´re height´s. I have this in my code, but it´s not working, does anyone knows how to fix it?:
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc]init];
switch (indexPath.section)
{
case 1:
cell = [tableView dequeueReusableCellWithIdentifier:#"cell1"];
return 743.0f;
break;
case 2:
cell = [tableView dequeueReusableCellWithIdentifier:#"cell2"];
return 300.0f;
}
}
Thanks for your time.
It looks like you are trying to use this method for purposes it wasn't designed for...
you'll want to override the method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section)
case 1:
static NSString *CellIdentifier = #"cell1";
break;
case 2:
static NSString *CellIdentifier = #"cell2";
break;
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
return cell;
}
Only change the row height in the heightForRowAtIndexPath:
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section)
{
case 1:
return 743.0f;
break; //technically never used
case 2:
return 300.0f;
}
Check out this tutorial http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-1 its a good resource
I have these delegate method for a tableview inside a class:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [array1 count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewStylePlain reuseIdentifier:CellIdentifier] autorelease] ;
}
cell.textLabel.text = [array1 objectAtIndex:indexPath.row];
return cell;
}
if I have a single UITableView it's ok but if I have two UITableView? How Can I organize my code? with tag?
See how all the delegate methods have a tableView:(UITableView *)tableView in them?
You can define your table views in the header file and then just simply go: (assuming your table is called myTable)
if (tableView == myTable)
Then you can have as many table views as you like.
So for example:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [array1 count];
}
Becomes:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == myTable)
{
return [array1 count];
}
if (tableView == myTable2)
{
return [array2 count];
}
return 0;
}
My suggestion is having your data source act as a table view delegate, instead of your controller.
This is a design more closer to the Model-View-Controller pattern and will allow you much more flexibility and avoid checking for the specific value that the tableView argument has got in your delegate methods.
In your case, your delegate/data source would be a class with a member of type NSArray and also implementing the UITableViewDelegate protocol.
Yes you can do it with tag. Give your UITableViews the tags 1 and 2.
set up an switch:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewStylePlain reuseIdentifier:CellIdentifier] autorelease] ;
}
switch ([tableView tag]) {
case 1:{
[[cell textLabel] setText:#"First tag"]
break;
}
case 2:{
[[cell textLabel] setText:#"Second tag"]
break;
}
default:
break;
}
return cell;
}
Each of those methods passes in a reference to the table view that's calling it. I usually connect each table to an outlet in interface builder and conditionally return the datasource based on a comparison with tableView in the delegate methods and the outlet names. Doing so with a tag is also possible but messier and more open to complications when editing your view structure.