更改UITableViewCell中的对象还更改了重复使用的UITableViewCell中的对象 [英] Changing an Object in UITableViewCell is also changing the Object of a reused UITableViewCell

查看:82
本文介绍了更改UITableViewCell中的对象还更改了重复使用的UITableViewCell中的对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

大家好.我有一个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];

}

我的问题与过滤器按钮状态的更新有关.当我的应用加载表格视图时,一切看起来都很好.当我单击某个单元格行中的过滤器按钮时,该按钮的状态会正确更新,并且我的模型对象也正在正确更新,因为我从最终要更新的mapView中看到了预期的行为.

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

我知道这个问题与我的单元格被重用有关,并且我猜想对于不同的单元格行引用了相同的按钮.不过,我对这是怎么回事不知所措,因为我正在为每个单元格创建一个新的筛选器按钮,无论该单元格是否被重用,并将该单元格的filterButton属性重置为新创建的对象.例如,请注意,headCount属性的文本(也是在单元格笔尖中定义的UILabel)也被重新分配给每个单元格的新String对象,并且在每一行中都正确显示.

我已经为这个问题苦苦挣扎了几天.任何帮助或建议都将不胜感激.

解决方案

表视图缓存其单元格以允许您重用它们,无论何时调用dequeueReusableCellWithIdentifier:,您都可以这样做.您应该在tableView:cellForRowAtIndexPath:方法结束之前调用setSelected:,以将按钮的状态与对应于当前行的Person实例的状态同步.

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

Code updated to working version. Thanks again for the help :)

Hey guys. I have a UITableViewController set up to use a custom cell loaded from a nib. Here's my 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;
}

This seems to work fine for me, but one issue has come up with the code after the /* set filter button image */ comment.

The filter button is a UIButton in my custom cell nib that is supposed to reflect the state of a model array containing 'Person' objects, which have a field that can be toggled to represent whether they are being filtered or not.

The way that I allow a user to update this model object is through a custom delegate method on my top level controller, which, whenever the user clicks the filter button, updates the model and the state of the button, and additionally updates a mapViewController with some data to show based on the state of the model:

- (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];

}

My issue comes with the updating of the state for the filter button. When my app loads the tableview, everything looks fine. When I click a filter button in a certain cell row the state of the button updates correctly, and my model objects are also updating correctly since I see the expected behavior from the mapView which I'm ultimately updating.

However, the issue is that when I click on the filterButton in one cell row and then scroll down a few rows, I notice another filter button in a different cell now has the same state as the one I clicked a few rows above. If I scroll back up again, the original button I clicked 'on' now seems to be 'off' but the row below it now appears 'on'. Of course all this is affecting is the actual display of the buttons. The actual state of the buttons is consistent and working correctly.

I know this issue must have something to do with the fact that my cells are being reused, and I'm guessing somehow the same buttons are being referenced for different cell rows. I'm at a loss as to how this is happening though, since I'm creating a new filter button for each cell, whether the cell is reused or not, and resetting the filterButton property of the cell to be the newly created object. Notice for example that the text of the headCount property, which is a UILabel also defined in the cell nib, is also being reassigned to a new String object for each cell, and it is displaying correctly for each row.

I've been struggling with this problem for a few days now. Any help or suggestions at all would be really appreciated.

解决方案

Table views cache their cells to allow you to reuse them, which you're doing whenever you call dequeueReusableCellWithIdentifier:. You should call setSelected: before the end of your tableView:cellForRowAtIndexPath: method to synchronize the state of the button with the state of the Person instance that corresponds to the current row.

Another thing to consider is that creating new button instances each time you return a cell is pretty wasteful. Consider creating and configuring the buttons (setting titles, images, etc.) once per cell instance inside the if block where you're loading the cell from the nib file.

这篇关于更改UITableViewCell中的对象还更改了重复使用的UITableViewCell中的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆