Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView - When scrolling too fast, contents of last cell replaces first cell

This is probably caused by poor design, but when I scroll my table too fast, and then scroll back to the top, the view that is placed in the last table cell is also placed over the first table cell in the tableView.

I think it is probably caused by my use of if statements to put some static content in the first section and dynamic content in the second section.

Any help is appreciated.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Configure the cell...

    if (indexPath.section == 0) {

        if (indexPath.row == 0) {

            cell.selectionStyle = UITableViewCellSelectionStyleNone;
            UITextField *textField = [[UITextField alloc]initWithFrame:CGRectMake(10, 13, 282, 20)];
            textField.clearsOnBeginEditing = NO;
            textField.placeholder = @"enter template name";
            textField.font = [UIFont systemFontOfSize:15.0];
            textField.clearButtonMode = UITextFieldViewModeWhileEditing;
            textField.delegate = self;
            textField.text = [selectedTemplate name];
            [textField addTarget:self action:@selector(nameDidChange:) forControlEvents:UIControlEventEditingChanged];
            [cell.contentView addSubview:textField];

        } else {

            cell.textLabel.text =  @"Add a new question";
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

        }

    } else {

        NSString *label = [[sortedQuestions valueForKey:@"questionText"] objectAtIndex:indexPath.row];
        CGSize stringSize = [label sizeWithFont:[UIFont boldSystemFontOfSize:15] constrainedToSize:CGSizeMake(230, 9999) lineBreakMode:NSLineBreakByWordWrapping];

        UITextView *textV=[[UITextView alloc] initWithFrame:CGRectMake(5, 5, 230, stringSize.height+10)];
        textV.font = [UIFont systemFontOfSize:15.0];
        textV.text=label;
        textV.textColor=[UIColor blackColor];
        textV.editable = NO;
        textV.userInteractionEnabled = NO;
        textV.backgroundColor = [UIColor colorWithRed:0.97f green:0.97f blue:0.97f alpha:1.00f];
        [cell.contentView addSubview:textV];

        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

    }

    return cell;
}
like image 382
Brad Avatar asked Nov 02 '12 01:11

Brad


3 Answers

Here is what I ended up doing based on Wienke's recommendation. I added 3 prototype cells to my storyboard named Cell,Cell2,Cell3.

Relevant code:

static NSString *CellIdentifier = @"Cell";
static NSString *CellIdentifier2 = @"Cell2";
static NSString *CellIdentifier3 = @"Cell3";

UITableViewCell *cell;

if (indexPath.section == 0 && indexPath.row == 0) {

    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

} else if (indexPath.section == 0 && indexPath.row == 1) {

    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier2 forIndexPath:indexPath];

} else if (indexPath.section == 1) {

    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier3 forIndexPath:indexPath];

}

I also added this to check my dynamic cells and remove any lingering subviews that could be hanging around before adding the new subview.

    if ([cell.contentView subviews]){
        for (UIView *subview in [cell.contentView subviews]) {
            [subview removeFromSuperview];
        }
    }
like image 82
Brad Avatar answered Nov 07 '22 19:11

Brad


It looks like a cell that was layed out for, say, section 0 row 0 is being dequeued and used for, say, section 0 row 1, which does not replace all of the settings that were made for it when it was holding section 0 row 0 material.

You might want to consider using 3 separate cell identifiers, one for section 0 row 0, one for section 0 row 1, and one for all the rest. You'll need a preliminary set of if statements (or switch-case statements) so that you use the right identifier when calling dequeueReusableCellWithIdentifier:.

like image 1
Wienke Avatar answered Nov 07 '22 18:11

Wienke


I know this already has an accepted answer, but I figured I would go slightly deeper into the "why" this was happening, and the solution.

In layman's terms, the UITableView refreshing was lagging with the presentation speed asked. In other words, due to the rapid scroll, the table view was expected to repopulate the content faster than it could sort through the expected cell to reuse for it's respective index (row).

The simplest solution, which Wienke touched on, is to create different cell identifiers for the first, let's say three (3) cells. This will allow the table view to be able to clearly differentiate between the 3 cells, preventing any cell misplacement.

Perhaps the best approach here would be to assign relevant cell identifiers to each cell (depending on the context and the number of cells). This way the table view knows precisely which cell (with it's respective ID) goes where. Something as simple as the following:

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

    NSString *cellID = [NSString stringWithFormat:@"cell%lu", indexPath.row];
    __kindof UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];

    if (!cell) {

        // Create your cell here.
        cell = [...allocate a new cell...] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];

    }

    return cell;

}
like image 2
Will Von Ullrich Avatar answered Nov 07 '22 17:11

Will Von Ullrich