美文网首页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