Search code examples
iosobjective-cuitableviewnsindexpathheightforrowatindexpath

UITableView cell displaying wrong content after height change


I'm working on implementing a UITableView with three sections. The last row of the second section is supposed to display an instance of a UIPicker.

As such, I've changed the height for this particular row, as shown below:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    CGFloat rowHeight = self.tableView.rowHeight;
    if (indexPath.section == RedZoneSection && indexPath.row == MonitorConfigRow){
        rowHeight = 162;
        return rowHeight;
    }
    return rowHeight;
}

However, when I add that code, the first row ("Only Alert From") in the third section is adding a UISwitch to it's view which should not be there, as shown below:

enter image description here

Below is where I implement cellForRowAtIndexPath for the third section:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForTimeOfDayRestrictionsRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"SettingsRowCell";

    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
    }

    cell.backgroundColor = [UIColor colorWithRed:0.922 green:0.937 blue:0.949 alpha:1];

    switch (indexPath.row) {
        case HourTimeZoneRow:
            cell.textLabel.text = NSLocalizedString(@"Only Alert From", @"Only Alert Row");
            break;
        default:
            break;
    }
    return cell;
}

EDIT The specific UISwitch that is being shown again in the incorrect place is originally in the second cell in the first section of my table. Below is the implementation of cellForRowAtIndexPath for that section:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForSubscribedNotificationsRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"SettingsRowCell";

    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
    }

    cell.backgroundColor = [UIColor colorWithRed:0.922 green:0.937 blue:0.949 alpha:1];

    UISwitch *cellSwitch = nil;

    switch (indexPath.row) {
        case RowOne:
            cell.textLabel.text = NSLocalizedString(@"Row One", @"Row One");
            cellSwitch = [[UISwitch alloc] init];
            cell.accessoryView = cellSwitch;
            break;

        case RowTwo:
            cell.textLabel.text = NSLocalizedString(@"Row Two", @"Row Two");
                cell.accessoryView = cellSwitch;
                break;
 //            cell.textLabel.text = nil ;
            cell.accessoryView = UITableViewCellAccessoryNone;
            accessoryViewIsShowing = FALSE;
            break;
    }
    if (cell.accessoryView == nil) {
        NSLog(@"Cell accessoryView is nil");
    }
    else if (cell.accessoryView != nil){
        NSLog(@"Cell accessoryView is not nil");
    }

    NSLog(@"Section: %ld, Row: %ld", (long)indexPath.section, (long)indexPath.row);

    return cell;
}

Does anyone know why changing the height of a specific cell with cause incorrect content to be displayed in a cell in another section?


Solution

  • The problem is there because of an error related to recycling cells in the tableView:cellForSubscribedNotificationsRowAtIndexPath: method. Changing the height of one of the cells makes this error visible.

    You need to add code to the default case to remove accessories and cell text that you add for RowOne and RowTwo:

    default:
        cell.textLabel.text = nil;
        cell.accessoryView = nil;
        break;
    

    Otherwise, a "recycled" cell in rows other than RowOne and RowTwo, which used to be RowOne or RowTwo, would contain the old text and accessory view that was there before the cell has been recycled.

    Another alternative to solving this would be overriding prepareForReuse.