更改 UITableViewCell 中的对象也会更改重用的 UITableViewCell 的对象

发布于 2024-10-16 18:34:06 字数 3828 浏览 1 评论 0原文

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

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

这似乎对我来说工作得很好,但是在 /* set filter 按钮图像 */ 注释之后的代码出现了一个问题。

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

我允许用户更新此模型对象的方式是通过我的顶级控制器上的自定义委托方法,每当用户单击过滤器按钮时,该方法都会更新模型和按钮的状态,并另外更新 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 属性重置为新创建的对象。请注意,例如,headCount 属性的文本(也是在单元格笔尖中定义的 UILabel)也被重新分配给每个单元格的新 String 对象,并且每行都正确显示。

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

聽兲甴掵 2024-10-23 18:34:06

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

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

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.

沦落红尘 2024-10-23 18:34:06

这是当您在单元出列后更改单元视图结构时发生的典型问题,而您只能在分配/初始化阶段更改单元结构。在出队或分配/初始化之后,您只能自定义内容,而不能自定义结构。

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

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

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

因此,黄金法则是:在取出单元格后,切勿更改单元格结构。如果您需要更改结构,请使用不同的单元 ID。当然,单元的可定制性越高,重用它们的可能性就越大。

This is a typical issue that happens when you change the cell view structure after it has been dequeued, while you're allowed to change the cell structure only in the alloc/init stage. After dequeue or alloc/init you are allowed to customize the content only and not the structure.

In your case, when the cell (let's say it is row-0) is loaded from the nib, the internal subviews structure is created (as defined in the Nib) and filterButton instance is assigned to one of these subviews. But a few lines below you create a new UIButton and replace the filterButton instance with this new one, but the real button subview will remain the same! Now when you click a button, of course the "real" button (that is the button in the cell view hierarchy which has been originally created by the Nib) will be triggered, the callback called and the state changed.

Later, when you scroll up this row-0 cell, it is removed from screen and enqued and then re-used for another cell, let's say row-9. At this point, former cell row-0 is going to be reused for cell row-9, but setting filterButton has still no effect, as you're keep going using the original button loaded initially by the Nib for cell row-0. Of course you will see these buttons states to be messed during scrollings as they are reused by the queue mechanism differently each time (so row-0 --> row-9, later row-0 --> row-8 and so on).

The solution is simply to change the button status: [self.filterButton setSelected:NO|YES] and not change the cell view content.

So the golden rule is: NEVER change the cell structure after you've dequed it. If you need to change the structure then use DIFFERENT cell IDs. Of course the more customizable is the cell the easier is the possibility to reuse them.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文