Mixing Static and Dynamic Sections in a Grouped Table View

Mixing static and dynamic sections in a grouped table view

Dynamic prototype cells can behave like static ones if you just return the cell without adding any content in cellForRowAtIndexPath, so you can have both "static like" cells and dynamic ones (where the number of rows and the content are variable) by using dynamic prototypes.

In the example below, I started with a table view controller in IB (with a grouped table view), and changed the number of dynamic prototype cells to 3. I adjusted the size of the first cell to 80, and added a UIImageView and two labels. The middle cell is a Basic style cell, and the last one is another custom cell with a single centered label. I gave them each their own identifier. This is what it looks like in IB:

Sample Image

Then in code, I did this:

- (void)viewDidLoad {
[super viewDidLoad];
self.theData = @[@"One",@"Two",@"Three",@"Four",@"Five"];
[self.tableView reloadData];
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 1)
return self.theData.count;
return 1;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0)
return 80;
return 44;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;

if (indexPath.section == 0) {
cell = [tableView dequeueReusableCellWithIdentifier:@"TitleCell" forIndexPath:indexPath];

}else if (indexPath.section == 1) {
cell = [tableView dequeueReusableCellWithIdentifier:@"DataCell" forIndexPath:indexPath];
cell.textLabel.text = self.theData[indexPath.row];

}else if (indexPath.section == 2) {
cell = [tableView dequeueReusableCellWithIdentifier:@"ButtonCell" forIndexPath:indexPath];
}

return cell;
}

As you can see, for the "static like" cells, I just return the cell with the correct identifier, and I get exactly what I set up in IB. The result at runtime will look like your posted image with three sections.

How to have, in a grouped style, two contents : dynamic and static

Put the button in a tableFooterView.

https://github.com/dpfannenstiel/symmetrical-octo-journey

UITableView Mix of Static and Dynamic sections Cells ? throw error -[__NSSingleObjectArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'

index 1 beyond bounds [0 .. 0] suggests something is trying to read users before it has been populated, so it's still in the initial empty state:

var users = [User]()

The only time you try to index users is when configuring a cell in your "Dynamic" section, which suggests you've misinformed the tableview about how much data you've got. Also, I don't see any code to report 2 as the number of sections.

Observation: Deferring to super for any UITableViewDelegate methods seems odd. How would it be in a better position than your own code to know what cell to provide or how many rows are in a section?

Also, consider using the tableView.dequeueReusableCell(withIdentifier: String, for: IndexPath) variant as it always returns a cell.

UITableView Mix of Static and Dynamic Cells?

As you stated you can't mix static and dynamic cells. However, what you can do is break up the content into different data arrays that correspond to each group. Then break the table up into difference sections and load the data from the correct array in cellForRowAtIndexPath:.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = @"CELLID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];

switch (indexPath.section) {
case 0:{
cell.textLabel.text = self.arrayOfStaticThings1[indexPath.row];
}break;

case 1:{
cell.textLabel.text = self.arrayOfDynamicThings[indexPath.row];
}break;

case 2:{
cell.textLabel.text = self.arrayOfStaticThings2[indexPath.row];
}break;

default:
break;
}

return cell;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch (section) {
case 0:{
return self.arrayOfStaticThings1.count;
}break;

case 1:{
return self.arrayOfDynamicThings.count;
}break;

case 2:{
return self.arrayOfStaticThings2.count;
}break;

default:
return 0;
break;
}
}

UITableView dynamic grouped cells

Here is the code TableViewDemo You basically have to use below two methods.

 override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 4
}

and this for title of header -

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "your title" // Use the titleForHeaderInSection index
}

UITableView: Handle cell selection in a mixed cell table view static and dynamic cells

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
}

Edit static grouped table view with multiple sections in Xcode Storyboard

I'm not sure if I quite understand the question, but if you're asking if you can do everything in Interface Builder, the answer is yes. After you've set the table content to 'static cells' in the Attribute Inspector, set the number of sections to the number of data sections you want, then modify each section by selecting the section, changing the header, footer, and number of rows. You can also quickly edit the header by double-clicking on it.

Each row in the section can be modified in the same way.



Related Topics



Leave a reply



Submit