reloadRowsAtIndexPaths 一般用于刷新一组 cell,笔者在使用过程中发现,调用该方法后 Tableview 并不是刷新 cell,而是会重新创建 cell。
并且 cell 的重用会发生错乱,在 IndexPath [0, 0] 下创建的 cell 会在 IndexPath [0, 1] 下被重用。
比如一个常见的需求,在 TableView 第一行显示头像,第二行显示昵称。假设这两条信息是单独从服务器获取的。
模拟网络请求代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
_titles = @[@"Avator", @"Nickname"];
[self.tableView registerClass:[SFTableViewCell class] forCellReuseIdentifier:kCellIdentifier];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDelayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_avator = [UIImage imageNamed:@"cat"];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDelayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_nickName = @"Smallfly";
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:1 inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
});
});
}
但实际的的显示结果却是这样:
再来看一下 TableView DataSource 的实现:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Why reloadRowsAtIndexPaths [0,0] returned cell is nil?
SFTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier forIndexPath:indexPath];
cell.textLabel.text = _titles[indexPath.row];
if (indexPath.row == 0) {
[self configureCell:cell indexPath:indexPath];
} else {
NSString *nickName = _nickName ?: @"nickname";
cell.detailTextLabel.text = nickName;
}
return cell;
}
在 indexPath.row == 0 的时候为 cell 添加头像:
- (void)configureCell:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath {
for (UIView *sv in cell.contentView.subviews) {
if ([sv isKindOfClass:[UIImageView class]]) {
[sv removeFromSuperview];
}
}
UIImage *avator = _avator ?: [UIImage imageNamed:@"user_profile_avatar"];
UIImageView *avatorImageView = [[UIImageView alloc] initWithImage:avator];
// configure imageView...
[cell.contentView addSubview:avatorImageView];
}
为防止重复的添加 imageView ,先将存在的头像移除。
进行一番调试之后笔者发现,在请求前,两个 cell 的地址为:
<SFTableViewCell: 0x7fb1bf826c00> // indexPath [0, 0]
<SFTableViewCell: 0x7fb1be00fc00> // indexPath [0, 1]
在请求后调用 reloadRowsAtIndexPaths 两个 cell 的地址为:
<SFTableViewCell: 0x7fb1be001600> // indexPath [0, 0]
<SFTableViewCell: 0x7fb1bf826c00> // indexPath [0, 1]
存在诡异的地方:
- 在 reloadRowsAtIndexPaths 是 indexPath [0, 0] 的 cell 被重新创建了。
- indexPath [0, 1] cell 地址等于 indexPath [0, 0] cell 的地址。
因为以上两个问题,导致在刷新第二行 cell 时出现了原本在第一行的默认头像,如果用 reload 刷新整个 TableView 则不会有这样的问题。
当然,最好的方式是自定义头像 cell,为方便起见使用这种方式,发现了这个问题。如果有大神知道原理,请指点一二 。
参考 Demo 地址 UITableViewBug