Uitableview Mix of Static and Dynamic Cells

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 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.

mix static and dynamic cell in swift

You have a couple options...

  1. Use a .tableHeaderView - you can use any normal UIView + subviews, labels, images, etc, etc, etc

  2. Create a second prototype cell, and use that cell as your "first row". You can lay it out in Storyboard however you want... just because it is a prototyped cell, doesn't mean you have to change anything when you use it.

Method 2 will end up looking similar to this:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

// if it's the first row, show the prototype cell with
// identifier "StaticCell"
if indexPath.row == 0 {

let cell = tableView.dequeueReusableCell(withIdentifier: "StaticCell", for: indexPath)
return cell

}

// it's not the first row, so show the prototype cell with
// identifier "cell"
//
// Note: you will need to "offset" the array index since the
// "2nd row" is indexPath.row == 1
let cell : TicketDetailTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TicketDetailTableViewCell

var dict = dictionary[indexPath.row - 1]
cell.lblComment.text = dict["comment"] as? String
cell.lblDate.text = dict["date"] as? String

return cell
}

// you will also need to return +1 on the number of rows
// so you can "add" the first, static row
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dictionary.count + 1
}

Mix of static and dynamic table view cells iOS

You can subclass UITableViewController and override the dataSource and delegate methods to achieve this nicely.

The key is to forward calls on to super where the static content is required and do your own thing when dynamic content is required.

For example, use IB to define a UITableViewController with your static content in the normal way but then subclass as follows to add a single extra section with dynamic content:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [super numberOfSectionsInTableView:tableView] + 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(section < [super numberOfSectionsInTableView:tableView]) {
return [super tableView: tableView numberOfRowsInSection:section];
}
else {
return self.numberOfRowsInMyDynamicSection;
}
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.section < [super numberOfSectionsInTableView:tableView]) {
return [super tableVIew: tableView cellForRowAtIndexPath: indexPath];
}
else {
// do your own dynamic cell management;
}
}

//etc. for the other dataSource and delegate methods

I've used this technique to create a UITableViewController subclass that allows you to show/hide statically defined cells at runtime.

It takes a bit of effort to implement all the dateSource/delegate methods, but you end up with a really handy UITableViewController subclass.

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
}


Related Topics



Leave a reply



Submit