美文网首页iOS Developer
【iOS】关于tableview单选cell的一点尝试和见解

【iOS】关于tableview单选cell的一点尝试和见解

作者: 清無 | 来源:发表于2017-03-21 15:30 被阅读1000次

2017.09.19更新

  • 最近发现可以利用cell.isSelected属性更好更方便地去做单选cell的设置
不错的单选效果

原理分析

  • 其实我不推荐使用cell.isSelected属性的原因是,cell的单选和多选,本质上是由tableview来统一控制管理的,手动设置selected属性本质上是并不能起到“选中”cell的作用;
  • 解决方法是在viewWillAppear,或tableView的网络数据加载完成后的回调里,通过
let selectedRow = IndexPath(row: 0, section: 0)
tableView.selectRow(at: selectedRow, animated: false, scrollPosition: .none)

这种方式来设置最初的默认选中行;

  • 另外tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell这个方法其实是tableview的数据源方法,在里边做cell的selected是本身不合理的,因为cell还没有完全被return和display,更别提操作cell了;

代码实现

  • 重写cell的setSelected方法
override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
    
    accessoryType = selected ? .checkmark : .none
}
  • 设置默认选中的行
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

// 由于我这个例子中的数据都是dead,所以tableView很快创建好,你可以在网络数据请求的回调中这样操作
    tableView.reloadData()
    tableView.beginUpdates()
    tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: .top)
    tableView.endUpdates()
}

旧方法

先看一个demo:

单选

Note: 功能上看起来是没什么问题,可以实现正常的单选选中,但大家有没有发现一个细节性的bug:选中后面的cell时候,tableview会跳一下,然后到自动滚到顶部了

原因

  • 实现上通过data的属性,控制当前indexPath对应cell的选中状态;
  • 每次选中cell,让当前cell所对应的数据属性selected变成true,其他所有都为false;
  • reloadData();//这才是导致选中后,tableview会“跳”一下的根本原因
typealias Item = (title: String, selected: Bool)
private var data = [Item]()

// 选中
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
     tableView.deselectRow(at: indexPath, animated: true)
     
// 改变数据源控制属性值
     data[indexPath.row].selected = true
// 刷新表格
    tableView.reloadData()
 }

// 复用
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        
// 当前选中的cell样式
        cell.accessoryType = data[indexPath.row].selected ? .checkmark : .none
        
        return cell
}

改进

尝试一:
  • 用一个私有全局变量记录上一次选中的cell的indexPath;
  • 当cell选中的时候,获得选中的cell,并立即设置其选中的样式,然后将上一次选中的cell样式置反;
  • 不要reloadData();

属性设置

// 默认选中第一行
private var selectedIndex = IndexPath(row: 0, section: 0)

// 选中cell样式设置
private func setAccessoryTypeOfCellAt(index: IndexPath, selected: Bool){
        let cell = tableView.cellForRow(at: index)
        cell?.accessoryType = selected ? .checkmark : .none
}

选中

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
// 取消选中上一个cell
        setAccessoryTypeOfCellAt(index: selectedIndex, selected: false)
// 选中当前cell
        setAccessoryTypeOfCellAt(index: indexPath, selected: true)
// 记录indexPath,便于cell复用时能正确显示其样式
        selectedIndex = indexPath
}

复用

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        
        cell.textLabel?.text = data[indexPath.row]
        cell.accessoryType = indexPath == selectedIndex ? .checkmark : .none

        return cell
}

改进效果

不会跳动

见解

  • 如果cell比较少,不足以达到满屏复用的时候,可以考虑第一种reloadData的方式;
  • 如果cell较多,则要考虑单选“跳动”的问题;
  • 如果你有更好的方式,欢迎留言评论,我便于扩充整理;

相关文章

网友评论

    本文标题:【iOS】关于tableview单选cell的一点尝试和见解

    本文链接:https://www.haomeiwen.com/subject/wilgnttx.html