面试题答案
一键面试导致Cell显示错乱的原因分析
- 数据绑定问题:当复用Cell时,如果没有正确地重新设置Cell内各个视图的数据,就可能显示上一次复用Cell时的数据,从而导致错乱。例如,一个Cell用于显示用户头像和用户名,如果复用Cell时没有更新用户名,就会显示之前用户的名字。
- Cell类型管理不当:在有多种类型Cell的情况下,如果没有正确区分和管理不同类型的Cell复用,可能会用错Cell,导致显示异常。比如,列表中有普通文本Cell和图文混排Cell,如果复用逻辑错误,图文混排Cell可能被当作普通文本Cell使用。
- 异步加载数据:如果在Cell中异步加载数据(如加载网络图片),当Cell复用后,之前异步任务可能还在执行并更新Cell,而此时Cell可能已经显示新的数据,就会出现显示错乱。例如,Cell A加载图片任务还未完成,Cell A复用为Cell B后,图片加载完成并更新到Cell B上。
确保Cell正确显示的解决方案
- 正确绑定数据:每次复用Cell后,都要重新设置Cell内各个视图的数据。在
cellForRowAtIndexPath:
方法中,获取复用Cell后,根据数据源重新设置Cell中的所有数据。 - Cell类型管理:
- 使用
UITableViewDelegate
的tableView:cellForRowAtIndexPath:
方法中的indexPath
来判断Cell类型。 - 在注册Cell时,为不同类型的Cell使用不同的
identifier
。例如:
- 使用
// 注册普通文本Cell
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"PlainCell"];
// 注册图文混排Cell
[self.tableView registerClass:[CustomImageTextCell class] forCellReuseIdentifier:@"ImageTextCell"];
- 在`cellForRowAtIndexPath:`方法中根据Cell类型获取复用Cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PlainCell" forIndexPath:indexPath];
cell.textLabel.text = self.plainTextData[indexPath.row];
return cell;
} else {
CustomImageTextCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ImageTextCell" forIndexPath:indexPath];
cell.imageView.image = [UIImage imageNamed:self.imageTextData[indexPath.row][@"imageName"]];
cell.textLabel.text = self.imageTextData[indexPath.row][@"text"];
return cell;
}
}
- 处理异步加载数据:
- 取消复用Cell上正在进行的异步任务。可以在Cell类中添加一个取消任务的方法,在复用Cell前调用。例如:
@interface CustomImageCell : UITableViewCell
@property (nonatomic, strong) NSURLSessionDataTask *imageLoadTask;
- (void)cancelImageLoadTask;
@end
@implementation CustomImageCell
- (void)cancelImageLoadTask {
[self.imageLoadTask cancel];
self.imageLoadTask = nil;
}
@end
在cellForRowAtIndexPath:
方法中获取复用Cell后调用:
CustomImageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ImageCell" forIndexPath:indexPath];
[cell cancelImageLoadTask];
// 重新开始加载图片任务
NSURL *imageURL = [NSURL URLWithString:self.imageData[indexPath.row]];
cell.imageLoadTask = [[NSURLSession sharedSession] dataTaskWithURL:imageURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data &&!error) {
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
if ([[tableView indexPathForCell:cell] isEqual:indexPath]) {
cell.imageView.image = image;
}
});
}
}];
[cell.imageLoadTask resume];
- 使用`indexPath`来判断Cell是否还是原来的Cell。在异步任务完成回调中,通过`indexPathForCell:`方法获取Cell对应的`indexPath`,与当前`indexPath`比较,只有相同才更新Cell,避免更新到错误的Cell上。