Robert J. Clegg
Robert J. Clegg

Reputation: 7360

UITableView with dynamic sections and custom cells

My data structor looks like this:

Inside an NSArray I have a few objects from different classes. How the array looks like if something like this:

Let's say myArray has 3 objectAs. Each objectA and an NSArry that has objectBs

Now at runtime I do not know the array counts.

My UITableView displays one objectA per section. Each section has 1 row. Unless my objectA has an NSArray of objectBs with a count of greater than 0.

So the UITableView would look like this if all my objectA's has no arrays of objectB's in them.

Section 1: Row 1: `objectA instance
Section 2: Row 1: `objectA instance
Section 3: Row 1: `objectA instance

Lets say the 3rd objectA has an array of objectB's with a count of 1. My UITableView would look like this:

Section 1: Row 1: `objectA instance
Section 2: Row 1: `objectA instance
Section 3: Row 1: `objectA instance
Section 3: Row 2: `objectB instance

Now I am using two different UITableViewCells for each object so objectA would use CustomCell1 and objectB would use CustomCell2

Here is where I am getting stuck - I need to return the correct cell to the correct section / row.

So I made sure my UITableView knows what to expect.

I wrote the methods like so:

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    ObjectA *objectA = self.myArray[section];

    return [objectA.objectBArray count] +1;
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [self.myArra count];
}

So numberOfRowsInSection takes a look at the objectBs array and returns its count + 1 so that even if its 0 there is always one row. (I need at least one row for objectA in the current section.

numberOfSectionsInTableView is straight forward - one section for each objectA in self.myArray

This seems to do exactly as I need.

I then got stuck on cellForRowAtIndexPath method.

This is how far I got:

 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        CustomCell1 *cell1 = [tableView dequeueReusableCellWithIdentifier:@"cell1"];
        CustomCell2 *cell2 = [tableView dequeueReusableCellWithIdentifier:@"cell2"];


        ObjectA *objectA = self.myArray[indexPath.section]; //Each section has one ObjectA. 

        //Make sure we have some objectB's to display first 
        if ([objectA. objectBArray count] > 0){
            ObjectB *objectb = (ObjectB*) objectA.objectB[indexPath.row]; 

          //if the current section (section 3) has a indexPath 0 already 
            // index path 1 would have the first objectB 

        }

        cell1.objectA = objectA;
        cell2.objectB = objectB;

      //Now return the correct cell for the current section / index path
    }

So I need to check if the current section already has valid cell1 object if it does I then need to return cell2 to the second or third row. for objectB As row one must always have a cell1.

I know I am almost there. I just can't figure out how to return the correct cell.

Update

So the cellForRowAtIndexPath method is only being called three times. Here is what I did in numberOfRowsInSection method to make sure it returns the right number of rows:

    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    ObjectA *objectA = self.myArray[section];
    NSLog (@"Section:%ld", (long)section);
    NSLog(@"Returning %ld", (long)[objectA.objectBArray count]+1);


    return [objectA.objectBArray count] +1;
}

My console shows this:

2014-07-16 19:36:17.488 App[15473:60b] Section:2
2014-07-16 19:36:17.488 App[15473:60b] Returning 2
2014-07-16 19:36:18.128 App[15473:60b] Section:0
2014-07-16 19:36:18.128 App[15473:60b] Returning 1
2014-07-16 19:36:19.063 App[15473:60b] Section:1
2014-07-16 19:36:19.063 App[15473:60b] Returning 1

Any ideas?

Upvotes: 1

Views: 1025

Answers (2)

Robert J. Clegg
Robert J. Clegg

Reputation: 7360

This is an answer to the second issue I was having. After setting breakpoints in my view controller - I noticed my tableView was already loading cells before my call to [self.tableView reloadData];

Not sure why that was happening so a quick nil-check in my rows for items method solved the issue:

if the main array had no objects - return 0. If it did - return objectBArray count + 1; Now the cellForRowAtIndexPath is called the correct number of times.

Upvotes: 0

Automate This
Automate This

Reputation: 31364

If there will always only be one objectA in each section then you can test if your indexPath.row is anything larger then zero (not the first row).

See if this does the trick:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomCell1 *cell1 = [tableView dequeueReusableCellWithIdentifier:@"cell1"];
    CustomCell2 *cell2 = [tableView dequeueReusableCellWithIdentifier:@"cell2"];

    ObjectA *objectA = self.myArray[indexPath.section]; //Each section has one ObjectA.
    cell1.objectA = objectA;

    //Check if not first row and objectB's exist
    if (indexPath.row > 0 && [objectA. objectBArray count] > 0){
        ObjectB *objectb = (ObjectB*) objectA.objectB[indexPath.row];
        cell2.objectB = objectB;
        return cell2;
    }

    return cell1;
}

Upvotes: 1

Related Questions