【问题标题】:Deadlock on UITableViewCell layoutSubviews when assigning accessoryView分配附件视图时UITableViewCell layoutSubviews死锁
【发布时间】:2013-08-30 23:54:51
【问题描述】:

您好,我在我的应用程序上发现了一个问题,可能并不严重但很奇怪。

我的 viewController 中有一个开关,我们称它为 theSwitch,当我填充单元格时,在 indexPath (0,0) 上我将该开关作为附件视图。

单元格的标题会根据开关的状态而变化

//Code simplified for simplicity 
if(indexPath.section == 0 && indexPath.row == 0) {
     cell.textLabel.text = self.filtersActivatedSwitch.on ? NSLocalizedString(@"Filters activated", nil) : NSLocalizedString(@"Filters deactivated", nil);
     cell.accessoryView = self.filtersActivatedSwitch;
}

当开关被按下时,这个方法被调用

-(void)switchActivated {
     [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:VisibilityFilterTableViewSectionMasterFilter]];
}

这将创建一个死锁,因为在方法 cellForRowAtIndexPath 上,当我尝试使单元格出列时,它将创建一个新的(经过测试,我认为它使用了我想重新加载的相同单元格更有意义)并且在单元格的配置中,开关将被添加为附件视图...

这就是棘手的地方......

BadPirate 的回答解释了何时在 tableviewcell 上调用 layoutSubview

When is layoutSubviews called?

-addSubview 导致 layoutSubviews 在被添加的视图、它被添加到的视图(目标视图)以及目标的所有子视图上调用

我认为 removeFromSupperview (假设)也应该发生同样的情况,所以新单元格有 layoutSubview 调用..它检查附件视图,看到它不是 nil 所以它添加它作为子视图,另一方面在旧的单元格(由于任何原因没有出队,所以我无法清除附件视图)layoutSubviews 被调用,因为 removeFromSuperview (再次假设)它检查附件视图不为零,它将开关添加为子视图......并重复永远。

我的问题是……为什么表格视图不使用已经出列的单元格?两个 tableviewcells 具有相同的附件视图这一事实显然会使我的应用程序崩溃。

或者我用错了附件视图?

干杯!

编辑: 这是prepareForReuse上的代码

- (void)prepareForReuse {
  [super prepareForReuse];

  for (UIView *view in self.contentView.subviews) {
      [view removeFromSuperview];
  }

  for (UIView *view in self.imageView.subviews) {
      [view removeFromSuperview];
  }

  self.textLabel.text = nil;
  self.detailTextLabel.text = nil;
  self.imageView.image = nil;
  self.imageView.tag = 0;
  self.accessoryView = nil;
}

【问题讨论】:

    标签: ios objective-c uitableview cocoa-touch uikit


    【解决方案1】:

    单元格被重复使用,因此滚动到屏幕外的单元格可以在另一个位置重复使用,您添加的任何子视图仍然存在。在特定的 indexPath 添加附件视图时,您需要在其中有一个“else”子句来告诉其他单元格没有该视图:。我不知道你在 cellForRowAtIndexPath: 中还做了什么,但是在我的测试方法中,我根本不需要使用 prepareForReuse 方法。我不确定我是否理解为什么在 switch 方法触发时会出现死锁,但如果你在 reloadRowsAtIndexPaths:withRowAnimation: 方法中将动画设置为 UITableViewRowAnimationNone,一切都会正常工作。

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    
        if(indexPath.section == 0 && indexPath.row == 0) {
            cell.textLabel.text = self.filtersActivatedSwitch.on ? NSLocalizedString(@"Filters activated", nil) : NSLocalizedString(@"Filters deactivated", nil);
            cell.accessoryView = self.filtersActivatedSwitch;
        }else{
            cell.textLabel.text = self.theData[indexPath.row];
            cell.accessoryView = nil;
        }
    
        return cell;
    }
    
    -(void)switchActivated {
    
        [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
    }
    

    【讨论】:

    • cell.accessoryView = UITableViewCellAccessoryNone 是错误的,这是一个枚举,accessoryView 需要一个对象......你可以传递 nil (这就是我在 if 之前所做的......即使在没有滚动的情况下也会发生这种情况我只打印 1 个单元格然后 updaterows 发生死锁(实际上我覆盖了 prepareForReuse 并将所有附件视图设置为 nil)
    • @Heavy_Bullets,是的,对不起,我在想附件类型,而不是视图。您在 cellForRowAtIndexPath 中发布的代码是什么?如果您还实现了 prepareForReuse,则应该发布该方法以及整个 cellForRow 方法。你所说的死锁是什么意思——你看到了什么?
    • thx,检查编辑,不是很花哨,只是将 tableviewcell 设置为尽可能裸(在 cellforrow 上它将被重新填充)
    • @Heavy_Bullets,我已经编辑了我的帖子以展示对我有用的内容——主要是当你重新加载行时没有动画。
    • 这个!...非常感谢..你帮我省了很多麻烦..+1并接受了答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 1970-01-01
    • 2012-03-14
    • 1970-01-01
    相关资源
    最近更新 更多