I am trying to mix dynamic and static cells in a grouped table view: I would like to get two sections with static cells at the top followed by a sec
For static cells you need to call super method. I presume that you will be extending UITableViewController. See below
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell;
/* Detect cell is static or dynamic(prototype) based on the index path and your settings */
if ("Cell is prototype")
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
else if ("Cell is static")
cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
// Modify cell properties if you want
return cell;
}
Also for more information on mixing cells see Mixing static and dynamic table view content discussion on apple forums.
[EDIT]
If you haven't read apple link above then please do so carefully. For dynamic content you will build the cell first time with the given identifier in the code itself. Next time on wards you will dequeue the cell rather than building it! It's the same old way.
Moreover, remember that the static data source thinks there are only 2 sections(for example). You can't ask it about section 2, because it thinks there are only sections 0 and 1. If you're going to be inserting dynamic content anywhere but the end of the table, you need to lie to super when you forward the data source methods. Your numberOfRowsInSection: method, for example, should look something like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 1) {
return 1;
}
else if (section > 1){
section--; // Basically you are forming sections for dynamic content
}
return [super tableView:tableView numberOfRowsInSection:section];
}
The key is making the adjustment for your dynamic content so that the static data source still gets the values it expects
[EDIT]
Here is the complete working example and tested. Just copy past all of following methods in your code and it should work straight way. You have to override all the tableview methods when you have mixed cells. Return the total number of static and dynamic sections in "numberOfSectionsInTableView" as per your requirements, and then in each of the remaining methods; if the section or row in question is static just call through to super, if it's dynamic pass back the relevant details as you would in a normal UITableViewController subclass. In this example, I am simply returning nil or hard coded values.
For more information check this thread on apple forums.
- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3;
}
- (NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return nil;
}
- (NSString*)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
{
return nil;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
-(float)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 44.0f;
}
- (float)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
return 44.0f;
}
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 44.0f;
}
- (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
return nil;
}
-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
return nil;
}
-(int)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 5;
}
- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(section == 0)
return 2;
if(section == 1)
return 2;
return 5;
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.section <= 1)
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.textLabel.text = @"dynamic row";
return cell;
}
[EDIT]
You don't call super in didSelectRowAtIndexPath. It's straight forward. See below code.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int row = indexPath.row;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
//process the cell
}
BEST & YET EASIEST Way for using Custom Dynamic Cells with Static Cells:
1) Place Container View in the Header of a Dynamic TableView 2) Assign the Static TableView to this Container and Untick the "Scrolling Enabled"
This can be simply solved by adding "fake" sections for the ones that will have dynamic cells. The only thing that will have to change is the cellForRowAt function, where you dequeue the dynamic cells for the "fake" sections instead of letting them be used from what you have in the storyboard. And for the rest of the cells u call
super.tableView(tableview, cellforRowAt: indexPath)
No need to override all the functions of the tableViewController
Thank you applefreak for comprehenisve answer. I'll add my suggestion here that you don't need any section calculations if you add a dummy sections for the static ones. You can even add placeholder elements there. This way you don't have to recreate NSIndexPath
s. One important thing is to override each and every method that uses NSIndexPath
s as these would crash the app due to array index mismatch.
Also, I gave up using the dequeue mechanism completely and created the cell statically as there weren't that many cells for the dynamic content I had. This makes tableView:cellForRowAtIndexPath:
very simple.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
return _locationCells[indexPath.row];
}
else
{
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
}
Just remember the placeholder sections.
UITableViewCell *cell = [self dequeueReusableCellWithIdentifier:@"cellIdentify"];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cellIdentify"];
Just you dont call alloc. If you dont want to reuse, just dont call dequeueReusableCellWithIdentifier.
If there is no cell to dequeue, then cell will be nil, and therefore you have to alloc/init a new one. Please, check the value of cell after you try to dequeue one, and create a new cell if it is nil.