在UITableViewCell中更改对象也正在更改重用的UITableViewCell的Object

时间:2011-02-08 19:22:02

标签: objective-c uitableview uibutton nib

代码已更新为工作版本。再次感谢您的帮助:)

嘿伙计们。我有一个UITableViewController设置使用从笔尖加载的自定义单元格。这是我的cellForRowAtIndexPath:

    // Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *cellIdentifier = @"PeopleFilterTableViewCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"PeopleFilterTableViewCell" owner:self options:nil];
        cell = peopleFilterTableViewCell;
        self.peopleFilterTableViewCell = nil;
    }

    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    PeopleFilterTableViewCell* tableViewCell = (PeopleFilterTableViewCell *) cell;

    /* Set direct button name */
    Person* personAtRow = [directsToShow objectAtIndex:indexPath.row];
    [tableViewCell.directButton setTitle:personAtRow.name forState:UIControlStateNormal];

    /* Set direct head count */
    tableViewCell.headcountLabel.text = [NSString stringWithFormat:@"%d", personAtRow.totalHeadCount];

    UIImage* unselectedImage = [UIImage imageNamed:@"filterButton.png"];
    UIImage* selectedImage = [UIImage imageNamed:@"filterButtonClosed.png"];

    UIButton* newFilterButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    /* Set filter button image */
    if(personAtRow.filtered){
        [newFilterButton setSelected:YES];
    } else {
        [newFilterButton setSelected:NO];
    }
    tableViewCell.filterButton = newFilterButton;

    return cell;
}

这似乎对我来说很好,但是在/ *设置过滤器按钮图像* /注释之后的代码中出现了一个问题。

过滤器按钮是我的自定义单元格中的UIButton,它应该反映包含“Person”对象的模型数组的状态,该对象具有可以切换的字段,以表示它们是否被过滤。 / p>

我允许用户更新此模型对象的方式是通过我的顶级控制器上的自定义委托方法,只要用户单击过滤器按钮,就会更新模型和按钮的状态,并另外更新一个mapViewController,其中包含一些基于模型状态显示的数据:

- (void)updateViews:(id)sender {
    UIImage* unselectedImage = [UIImage imageNamed:@"filterButton.png"];
    UIImage* selectedImage = [UIImage imageNamed:@"filterButtonClosed.png"];

    int row = [self rowOfCellSubView:sender];

    Person* personToFilter = [self.directsToBeShown objectAtIndex:row];
    NSLog(@"Filtering person with corpId: %@, name: %@", personToFilter.corpId, personToFilter.name);
    if (personToFilter.filtered){
        //update button image
        [sender setImage:unselectedImage forState:UIControlStateNormal];
        [sender setSelected:NO];
        //add person back.
        Person* directFiltered = [self.directsToBeShown objectAtIndex:row];
        directFiltered.filtered = NO;
        NSLog(@"Filtering person with corpId: %@, name: %@, filtered: %d", directFiltered.corpId, directFiltered.name, directFiltered.filtered);

    } else {
        //update button image
        [sender setImage:selectedImage forState:UIControlStateSelected];
        [sender setSelected:YES];
        //remove person.
        personToFilter.filtered = YES;

        NSLog(@"Filtering person with corpId: %@, name: %@, filtered: %d", personToFilter.corpId, personToFilter.name, personToFilter.filtered);
    }

    [self updateSitesToShow];
    [self.mapViewController performSelectorOnMainThread:@selector(updateDisplay) withObject:nil waitUntilDone:NO];

}

我的问题是更新过滤器按钮的状态。当我的应用程序加载tableview时,一切看起来都很好。当我单击某个单元格行中的过滤器按钮时,按钮的状态会正确更新,我的模型对象也会正确更新,因为我看到了mapView中我最终要更新的预期行为。

然而,问题是,当我在一个单元格行中单击filterButton然后向下滚动几行时,我注意到另一个单元格中的另一个过滤器按钮现在具有与我单击上面几行相同的状态。如果我再次向上滚动,我单击“打开”的原始按钮现在似乎是“关闭”,但它下方的行现在显示为“打开”。当然所有这些都影响了按钮的实际显示。按钮的实际状态是一致的,并且工作正常。

我知道这个问题必须与我的单元格被重用这个事实有关,而且我猜测不同的单元格行正在引用相同的按钮。我不知道如何发生这种情况,因为我正在为每个单元创建一个新的过滤器按钮,无论单元是否被重用,并将单元的filterButton属性重置为新创建的对象。例如,注意headCount属性的文本(也是在单元格nib中定义的UILabel)也被重新分配给每个单元格的新String对象,并且它正在为每一行正确显示。

我几天来一直在努力解决这个问题。任何帮助或建议都会非常感激。

2 个答案:

答案 0 :(得分:0)

表视图缓存其单元格以允许您重复使用它们,这是您在调用dequeueReusableCellWithIdentifier:时所执行的操作。您应该在setSelected:方法结束之前调用tableView:cellForRowAtIndexPath:,以将按钮的状态与对应于当前行的Person实例的状态同步。

要考虑的另一件事是每次返回单元格时创建新的按钮实例都非常浪费。考虑在您从nib文件加载单元格的if块内的每个单元实例中创建和配置按钮(设置标题,图像等)一次。

答案 1 :(得分:0)

这是在单元格视图结构出列后更改时发生的典型问题,而您只能在alloc / init阶段更改单元格结构。在dequeue或alloc / init之后,您可以仅自定义内容而不是结构。

在您的情况下,当从nib加载单元格(假设它是行-0)时,将创建内部子视图结构(如Nib中所定义),并将filterButton实例分配给其中一个子视图。但是下面几行你创建一个新的UIButton并用这个新的替换filterButton实例,但真正的按钮子视图将保持不变!现在,当您单击一个按钮时,当然会触发“真实”按钮(即最初由Nib创建的单元视图层次结构中的按钮),调用回调并更改状态。

稍后,当你向上滚动这个第0行的单元格时,它会从屏幕中移除并进入enqued,然后重新用于另一个单元格,让我们说第9行。此时,前单元格-0将重新用于单元格第9行,但设置filterButton仍然没有效果,因为您将继续使用Nib最初为单元格行0加载的原始按钮。当然,你会看到这些按钮状态在滚动过程中被混乱,因为它们每次都被队列机制重复使用(所以row-0 - > row-9,后面的row-0 - > row-8等等上)。

解决方案只是更改按钮状态:[self.filterButton setSelected:NO | YES]并且不更改单元格视图内容。

因此,黄金法则是:在你剔除之后永远不要改变细胞结构。如果您需要更改结构,请使用不同的单元ID。当然,单元格越可定制,重用它们的可能性就越大。