iosobjective-cuitableviewaccessorytype

UITableViewCell is nil when not visible in a large UITableView (Xcode, iOS)


I've made a large UITableView with 27 static UITableViewCells. When selecting a UITableViewCell it should set its accessoryType to UITableViewAccessoryCheckmark and the last selected UITableViewCell's accessoryType to UITableViewAccessoryNone:

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];

  // get an NSInteger from NSUserDefaults

  [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:savedInteger inSection:0] animated:NO scrollPosition:UITableViewScrollPositionTop];
  [self tableView:self.tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:savedInteger inSection:0]];
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  // get the NSInteger again

  UITableViewCell *lastCell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:savedInteger inSection:0]];
  UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

  lastCell.accessoryType = UITableViewAccessoryNone;
  cell.accessoryType = UITableViewAccessoryCheckmark;

  // save indexPath.row in NSUserDefaults and do some other stuff

This works fine when you select a UITableViewCell that is near to the last selected UITableViewCell but when the last selected UITableViewCell is about 11 rows or so away, the UITableViewCell *lastCell is nil and the checkmark doesn't disappear.

Why does it behave like that and how can I fix it? Thanks in advance.


Solution

  • cellForRowAtIndexPath returns nil if cell is not visible or index path is out of range.
    Therefore, check whether the currently selected cell is visible.

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
        // get the NSInteger from NSUserDefaults
        NSIndexPath *previousSelection = [NSIndexPath indexPathForRow:savedInteger inSection:0];
        if ([tableView.indexPathsForVisibleRows containsObject:previousSelection]) {
            UITableViewCell *lastCell = [tableView cellForRowAtIndexPath:previousSelection];
            lastCell.accessoryType = UITableViewAccessoryNone;
        }
    
        // save indexPath.row in NSUserDefaults
    
        UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
        cell.accessoryType = UITableViewAccessoryCheckmark;
    
        // do other stuff
    }
    

    Also, in order to ensure that the table view cells are created with the desired accessoryType in the first place, set the accessoryType of the table view cell in cellForRowAtIndexPath.

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
        // get the table view cell and set the details
    
        cell.accessoryType = UITableViewAccessoryNone;
    
        // get the NSInteger from NSUserDefaults
        if (indexPath.row == savedInteger) {
            cell.accessoryType = UITableViewAccessoryCheckmark; 
        }
    
        return cell;
    }