UITableViewCell 选择器 setSelected:animated: 被调用多次?

Posted

技术标签:

【中文标题】UITableViewCell 选择器 setSelected:animated: 被调用多次?【英文标题】:UITableViewCell selector setSelected:animated: gets called many times? 【发布时间】:2011-03-23 05:31:43 【问题描述】:

我在自定义 UITableViewCell 类中发现了 setSelected:animated: 的奇怪行为。我发现如果我点击表格中的一个单元格,这个函数会被多次调用。我想知道这是正常行为还是我的代码中的错误。

为了帮助调试,我修改了自定义 UITableViewCell 类实现中的 setSelected:animated: 函数,如下所示:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated 

[super setSelected:selected animated:animated];

// Configure the view for the selected state.
if (selected)
    NSLog(@"Yes %X", &self);
else
    NSLog(@"No %X", &self);


如果我在模拟器中单击一个单元格,我会在控制台中看到以下内容:

2011-03-22 22:05:26.963 marketPulse[3294:207] Yes BFFFDDD0
2011-03-22 22:05:26.964 marketPulse[3294:207] Yes BFFFDE30

你会认为我只会得到 1 个条目,因为我只点击了 1 个单元格。

如果我在那之后点击不同的单元格:

2011-03-22 22:07:11.014 marketPulse[3294:207] No BFFFD890
2011-03-22 22:07:11.016 marketPulse[3294:207] No BFFFDD00
2011-03-22 22:07:11.017 marketPulse[3294:207] Yes BFFFDDD0
2011-03-22 22:07:11.017 marketPulse[3294:207] Yes BFFFDE30

如果我连续点击同一个单元格 2 次,我会得到超过 2 个 Yes

2011-03-22 22:08:41.067 marketPulse[3294:207] Yes BFFFDDD0
2011-03-22 22:08:41.068 marketPulse[3294:207] Yes BFFFDE30
2011-03-22 22:08:41.069 marketPulse[3294:207] Yes BFFFDE30

点击同一个单元格的次数越多,得到的Yes越多,之后再点击不同的单元格,得到的No越多强>

我在NSLog前面放了一个断点,再看调试器,似乎所有重复的调用都来自同一个对象。

这是我的 tableView:cellForRowAtIndexPath: 函数的一部分,因此您可以看到我的细胞是如何被处理的:

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


static NSString *ContentCellIdentifier = @"newsTableCellContent";

UITableViewCell *cell;


//index of cell data in tableData
NSUInteger index = indexPath.row / 2;

...

//content of story
else if( [indexPath row] % 2 == 1 ) 

    cell = [tableView dequeueReusableCellWithIdentifier:ContentCellIdentifier];

    if (cell == nil) 
        NSArray *topLevelObjects = [[NSBundle mainBundle]
                                    loadNibNamed:@"newsTableCells"
                                    owner:nil options:nil];

        for (id currentObject in topLevelObjects) 
            if ( [currentObject isKindOfClass:[newsTableCellContent class]] ) 
                cell = currentObject;
                break;
            
        
    

    ((newsTableCellContent *)cell).content.text = [[tableData objectAtIndex:index] description];

   

return cell;

一切正常,因此很难判断重复调用 setSelected:animated: 是否是故意的。如果这是正常操作,我可以使用另一种方法,但我只想知道这是否会发生。

谢谢

【问题讨论】:

我还应该提到tableView:didSelectRowAtIndexPath: 函数仅在我单击单元格时被调用一次。 同样的事情发生在我的应用程序中。不知道为什么。 遇到同样的问题,改用轻击手势识别器。 我并不认为这是一个“问题”。如果这就是 UITableView 的工作方式,那就这样吧。您可以通过使用布尔值和什么不确保事情只被调用一次来轻松解决这个问题。 遇到同样的问题,真的很奇怪。滚动 UITableView 时也会调用 setSelected 【参考方案1】:

发生的只是UITableView 跟踪表格中选择了哪些单元格。

由于在滚动浏览大型表格视图时会重复使用单元格,因此表格视图必须将所选单元格的列表分开。不仅如此,每当它重用一个单元格时,它都必须设置其 selected 属性,因为它可能使用的是旧的、无效的 selected 状态。

当您点击一个单元格时,会发生几件事:取消选择之前选择的单元格(使用setSelected:)。新单元格突出显示。它被取消突出显示(至少如果您点击,而不是按住手指),并且调用 setSelected: 方法,因为选择了新单元格。就是这样一个。

第二个调用是一个延迟的执行调用,可能是从表格视图还不知道表格的最终状态的时候开始的。此调用转到_selectAllSelectedRows,顾名思义,它在所有选定的行上调用“setSelected:animated:”。这是第二个电话。这样做的原因最有可能解决由于表视图处于“转换”状态而导致的潜在问题,但谁知道呢。

这是否是错误取决于解释。重复调用的解决方法是:

if (self.selected == selected) return;

就在调用 super 之前(如果是 self.selected == selected,则不必调用 super)。

【讨论】:

这很奇怪,但我的应用程序仅在 iPad 上发生。在 iPhone 上,我没有任何 _selectAllSelectedRows 方法。但在 iPad 上,它会在每次调用 setSelected:animated: 后跟随,并违反我的选择。 有没有办法禁用 _selectAllSelectedRows(请尽快)?我不知道如何让自己摆脱这个问题..【参考方案2】:

如果您使用的是 iPad,这是正常现象。 (在 iPhone 上只调用一次)。

为了停止获取多个“setSelected:YES”或多个“setSelected:NO”,您所要做的就是:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    [tableView deselectRowAtIndexPath:indexPath animated:YES];

现在,单击任何单元格即可:

setSelected 的 1 个条目:YES 动画:NO tableView 的 1 个条目:didSelectRowAtIndexPath: setSelected 的 1 个条目:NO 动画:YES

因此,无论您做什么,通话现在都很稳定。

【讨论】:

【参考方案3】:

理想情况下,您不应在代码中的任何位置调用 setSelected。 UIKit 将负责调用它。

如果您想显示在​​ cellForRowAtIndexPath 方法中选择的单元格/行,只需调用

tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: .None)

对于那个特定的 indexPath。

除非你真的有意,否则永远不要显式调用 setSelected。

【讨论】:

【参考方案4】:

滚动表格时绝对应该调用它。单元格被重用,也就是说,如果您在不可见区域滚动单元格,将被重用并重新初始化,包括对 setSelected 的调用,它基本上是一个轻量级的属性设置器。

如果你真的想看看发生了什么,添加一个 NSLog 到 tableView:cellForRowAtIndexPath: 它将记录 indexPath 和返回的单元格。

整个日志应该让您很好地了解内部发生的事情以及原因。 我想它会是这样的(点击 IndexPath 1:1)

在 1:0 上给我单元格(之前选择的单元格)。 取消选择 1:0 再次给我 1:0 的单元格(取消选择后更新) 取消选择 1:0(更新此单元格上的选定标志并触发动画) 1:1 给我手机 选择 1:1 再次以 1:1 给我单元格(选择后更新) 选择 1:1(更新此单元格上的选定标志并触发动画)

再次单击选定的单元格只是略有不同 - 它不会触发取消选择,而是触发另一个更新。

【讨论】:

以上是关于UITableViewCell 选择器 setSelected:animated: 被调用多次?的主要内容,如果未能解决你的问题,请参考以下文章

UITableViewCell 子类抛出一个无法识别的选择器发送到实例

是否可以在自定义 UITableViewCell 中有一个按钮,该按钮引用其中的选择器

从 UITableViewCell 呈现 UIViewController:addChildViewController 无法识别的选择器发送到实例

使用 UITapGestureRecognizer、UIImageView 和 UITableViewCell 将参数传递给 Swift 中的选择器

UITableViewCell 中的 UITextField 抛出 -[UITextInputTraits length]:无法识别的选择器发送到实例

-[UITableViewCell setLayoutMargins:]:无法识别的选择器发送到 iphone 4 中的实例错误