0

I am trying to reduce duplicate code when laying out my tableview but running into lifecycle problems. Basically its that heightFroRowAtIndexPath is called before cellForRowAtIndexPath. Which is what should happen and I understand why.

But...

I have a cell that is laid out in a storyboard. It has an optional field. If the optional field is not in the data then I remove a label for that field. However I am removing that label in a custom cell implementation:

CustomCell (extends UITableViewCell)

- (void) configureCellForData: (Data *) data {
    if (data.optional) {
        self.optionalLabel.text = [data.optional];
    } else {
        [self.optionalLabel removeFromSuperview];
    }
}

Then in cellForRowAtIndexPath:

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

        CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:self.tableLayout[indexPath.section][indexPath.row]];
        [cell configureCellForData:self.data];
        return cell;
}

Which works great for setting up the cell. However the height is wrong if the optional field is removed, ie I need to adjust if the optional field was removed.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:self.tableLayout[indexPath.section][indexPath.row]];
        CustomCell *headerCell = (CustomCell *) cell;
        if (self.data.optional == nil) {
            return cell.bounds.size.height - headerCell.optionalLabel.bounds.size.height;
        }

        return cell.bounds.size.height;
    } 
}

It does not seem like much but I simplified my check to "data.optional == nil" and it is more complex than that and involves a DB call.

Is there a better way to set this up such that I don't have to make the check twice once when the height for cell is calculated and once when the cell is initialized?

lostintranslation
  • 23,756
  • 50
  • 159
  • 262
  • Instead of manually calculating the row height, can you use auto layout and let the content of the cell determine the height? http://stackoverflow.com/q/18746929/3711928 – j.f. Oct 05 '15 at 16:13
  • That looks promising, however having trouble implementing that. I still need to override heightForRowAtIndexPath to return 0 in some cases where I don't want a cell to show. Seems like when I override the heightForRowAtIndexPath I need to return something as the auto height does not work. – lostintranslation Oct 05 '15 at 16:50
  • Also after reading this it seems like this works great for dynamic content inside well defined labels. Not sure how to get it to size correctly when I remove labels from the view. – lostintranslation Oct 05 '15 at 16:52
  • Yes - if you use the auto height, you would no longer need `heightForRowAtIndexPath`. In cases where you don't want a cell to show, you could just remove it from your data source. And when it comes to hiding labels, instead of removing them completely from the view, you could update their individual heights to 0, effectively hiding them but also letting auto layout do its thing. – j.f. Oct 05 '15 at 16:57

1 Answers1

0

If you wanted to only check once you could store an array of booleans that stores whether or not the data is there or not. So, make the check for each row, store the result into the array, before you make the check next time, check to see if the array has an value for that cell, if it does, use that value, if not, make the database call.

Make sure that you only store values in the array index associated with the indexPath, and if the array is shorter than the indexPath you're at, you know you need to make the call and add the value into the array.

Edit: As I think more about it, I would put the bool value on the cell itself, and then just call cell.isDataAvailable (or whatever you want the value to be) in order to avoid the second call when you go to set the cell up, as you would have already checked this in heightForRowAtIndexPath.

Bill L
  • 2,576
  • 4
  • 28
  • 55