首页 > 怎么解决用Cell的accessoryType属性标记单元格之后,出现的重用问题,急求大神!

怎么解决用Cell的accessoryType属性标记单元格之后,出现的重用问题,急求大神!

新手一枚

问题如下:

我想用 TableView 做一个可以多项选择的表格,然后把 tableView的allowsMultipleSelection 属性设为了YES;

_tableView.allowsMultipleSelection = YES;

在 didSelectRowAtIndexPath 和 didDeselectRowAtIndexPath 方法里面使用了如下方法实现了点击单元格然后用check mark 标记的方式。

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    cell.accessoryType = UITableViewCellAccessoryCheckmark;

}

-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {

    [tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
}

但是运行起来发现单元格重用问题会影响到check mark的标记。

具体表现是点击第一个单元格之后,滚动页面,后面重用的单元格也会显示check mark的对号标记,但是单元格是未选中状态。

我怀疑是在 cellForRowAtIndexPath 这个函数上的调用出了问题。

请问有没有解决这个问题的更好的写法?

万分感谢。

附部分相关代码:

//tableVie的创建

_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, kTopViewHeight, kScreenWidth, kTableViewHeight) style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.allowsMultipleSelection = YES;
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:identifier];

//tableVieCell的创建

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];

    cell.textLabel.text = _dataArray[indexPath.row];

    return cell;
}

首先表扬一下,问题提得很棒~ 格式整齐,代码贴得很全,赞一个。

其次,这个思路不对。

你想要做一个多项选择的表格,最终目的不光是为了给选中的 cell 打勾,更是为了得到用户选择的数据。如果你得不到哪一项选中了、哪一项没选中的数据,就算打勾打正确了有什么用呢?

所以正确的思路就应该是围绕着记录选中/没选中的数据出发。在didSelectRowdidDeselectRow里操作数据的变化,然后通过 reloadData 显示出来。

  1. 设一个NSMutableArray属性,元素个数跟你的_dataArray一样,初始化里面存的都是0

    NSMutableArray* selectionArray = [NSMutableArray array];
    for (NSInteger i = 0; i < _dataArray.count; i++) {
        [selectionArray addObject:@(0)]; // 0 表示未选中,1 表示选中了
    }
    self.selectionArray = selectionArray;
  2. didSelectRowAtIndexPath:里:

    [self.selectionArray replaceObjectAtIndex:indexPath.row withObject:@(1)];
    [self.tableView reloadData];
  3. didDeselectRowAtIndexPath里:

    [self.selectionArray replaceObjectAtIndex:indexPath.row withObject:@(0)];
    [self.tableView reloadData];
  4. cellForRow里:

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    cell.textLabel.text = _dataArray[indexPath.row];
    NSInteger selected = [self.selectionArray[indexPath.row] IntegerValue];
    if (selected == 0) {
        cell.accessoryType = UITableViewCellAccessoryNone;
    } else {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    }
    return cell;

楼上说的我解释一下

他说不要用Cell的accessoryType来作为模型,而是应该有单独的数据对象

不过你这个情况不却是也不需要多写个容器纪录选中状态,因为UITableView本来就支持多选纪录

  1. cell 有 accessoryType 属性, 也有 selected 属性

  2. 你想用accessoryType属性 表现 被选中这个状态, 但是复用机制存在,产生了bug

所以你有两个解决方案

继承UITableViewCell,在setSeleted:animated:方法里面,根据选择状态,修改accessoryType

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    self.accessoryType = selected?UITableViewCellAccessoryCheckmark:UITableViewCellAccessoryNone;
    // Configure the view for the selected state
}

如果懒得继承,那就在- (UITableViewCell )tableView:(UITableView )tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath回调里面,根据cell的selected属性,修改吧,效果一样的,代码同1

祝你好运


MVCMVCMVC
模型模型模型
模型驱动视图模型驱动视图模型驱动视图
面向对象面向对象面向对象

重要事情说三遍

【热门文章】
【热门文章】