【问题标题】:How to have checkmarks icons (accessory view) on tableview already checked after cells rendered?如何在单元格渲染后检查表格视图上的复选标记图标(附件视图)?
【发布时间】:2018-12-12 13:33:49
【问题描述】:

我有模型数据,它有一个布尔值,表示是否应显示复选标记(accessoryType 为 .checkmark)...

例如,我希望在开始时选中五行中的两行(根据我所说的模型)...问题是,我可以显示复选标记,但是在我点击它们之后,切换不会不能正常工作:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    if let cell = tableView.cellForRow(at: indexPath) {
        cell.accessoryType = .checkmark
        model[indexPath.row].isCellSelected = true


    }
}
 func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {

    if let cell = tableView.cellForRow(at: indexPath) {
        cell.accessoryType = .none
        model[indexPath.row].isCellSelected = false


    }
}

And here is a cellForRowAt:

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let data = model[indexPath.row]
    let identifier = data.subtitle != nil ? kSubtitleID : kNormalID

    let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)

    cell.textLabel?.text = data.title
    cell.detailTextLabel?.text = data.subtitle

    return cell
}

我可以这样显示复选标记:

cell.accessoryType = data.isCellSelected ? .checkmark : .none

但是当我点击它时,它会导致它被选中(allowsMultipleSelection 设置为 true),它不会被切换,而是第一次停留。

这是我使用的模型。真的很简单:

struct CellModel{
    var title:String?
    var subtitle:String?
    var isCellSelected:Bool = false
}

【问题讨论】:

  • 只显示您使用的模型结构,可以通过cellForRowAtdidSelectRowAt..完成。
  • @vaibhav 我添加了模型示例。
  • 如果你还是卡住了,我把我的答案发过来看看。

标签: ios swift uitableview tableview accessorytype


【解决方案1】:

您应该只在tableView:didSelectRowAt: 中执行切换,然后重新加载必要的单元格。你应该省略tableView:didDeselectRowAt:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if let cell = tableView.cellForRow(at: indexPath) {
        // Toggle the value - true becomes false, false becomes true
        model[indexPath.row].isCellSelected = !model[indexPath.row].isCellSelected
        tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .none)
    }
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let data = model[indexPath.row]
    let identifier = data.subtitle != nil ? kSubtitleID : kNormalID

    let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)

    cell.textLabel?.text = data.title
    cell.detailTextLabel?.text = data.subtitle
    // Set the checkmark or none depending on your model value
    cell.inputAccessoryType = data.isCellSelected ? .checkmark : .none

    return cell
}

编辑:

仅用于单选 + 取消选择所选项目的能力:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // Loop through all model items
    for (index, item) in model.enumerated() {
        if (index == indexPath.row) {
            // Toggle tapped item
            item.isCellSelected = !item.isCellSelected
        } else {
            // Deselect all other items
            item.isCellSelected = false
        }
    }
    tableView.reloadData();
}

【讨论】:

  • 嘿,成功了,谢谢!我记得我之前对这种方法有过问题,但不能说是不是同样的情况(可能不是),但我还是跳过了它哈。
  • 没问题。确保您的 tableView:cellForRowAt: 方法反映了您的单元格可能与您的模型同步的任何状态,因为如果您仅在 tableView:didSelectRowAt: 方法中直接修改 UITableViewCell 对象并将单元格滚动到屏幕外,则将重新渲染所有更改没有反映在模型中的它们将丢失。可能有点混乱,抱歉。
  • 问题是,它在allowsMultipleSelection = true 时有效,但它不适用于具有单个选择的tableview...
【解决方案2】:

在您有一个保存布尔值的模型之前,您不必使用选择和取消选择 在tableview中的didselect方法

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {

        if model[index.row].isCellSelected == true {
            model[indexpath.row].isCellSelected = false
        }
        else {
            model[indexpath.row].isCellSelected = true
        }
        tableview.reloadData

}

在单元格中进行行检查

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if model[index.row].isCellSelected{
        cell.accessoryType = .checkmark
    }
    else {
        cell.accessoryType = .none
    }
}

【讨论】:

    【解决方案3】:

    我所做的是:

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            let data = model[indexPath.row]
            if let cell = tableView.cellForRow(at: indexPath) {
                cell.accessoryType = .checkmark
                model[indexPath.row].isCellSelected = true
    
                self.selectedData?(model.filter{$0.isCellSelected})
            }
        }
         func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
             let data = model[indexPath.row]
            if let cell = tableView.cellForRow(at: indexPath) {
                cell.accessoryType = .none
                model[indexPath.row].isCellSelected = false
    
                self.selectedData?(model.filter{$0.isCellSelected})
            }
        }
    

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
            let data = model[indexPath.row]
            let identifier = data.subtitle != nil ? kSubtitleID : kBasicID
    
            let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
    
            cell.textLabel?.text = data.title
            cell.detailTextLabel?.text = data.subtitle
    
            cell.accessoryType = data.isCellSelected ? .checkmark : .none
            return cell
        }
    

    但我必须首先设置实际选择了哪些单元格:

    func setSelectedCells(){
    
            let selectedIndexes = self.model.enumerated().compactMap { (index, element) -> IndexPath? in
                if element.isCellSelected{
                    return IndexPath(row: index, section: 0)
                }
                return nil
            }
    
            selectedIndexes.forEach{
                selectRow(at: $0, animated: false, scrollPosition: .none)
            }
        }
    

    然后在 viewDidAppear 中调用它,因为我必须确保表格已经绘制了它的内容(因为如果我们尝试一些尚不存在的东西(单元格),这将失败)。不是最好的方法,但它解决了 singlemultiple 选择与 initial states 的问题。

    【讨论】:

    • 它可以用更少的代码或方法来完成,以在任何状态下显示选定的选项。
    【解决方案4】:

    那么,cellForRowAt 方法就变得很简单了:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let data = model[indexPath.row]
        let identifier = data.subtitle != nil ? kSubtitleID : kNormalID
    
        let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
    
        cell.textLabel?.text = data.title
        cell.detailTextLabel?.text = data.subtitle
    
        cell.accessoryType = data.isCellSelected ? .checkmark : .none
    
        return cell
    }
    

    那么,didSelectRowAt 方法就变得很简单了:

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
    
        if let cell = tableView.cellForRow(at: indexPath) {        
            let isCellSelected = !(model[indexPath.row].isCellSelected)
            cell.accessoryType = isCellSelected ? .checkmark : .none
            model[indexPath.row].isCellSelected = isCellSelected
        }
    }
    

    请注意,我们必须切换标志并根据新值更新单元格和模型。

    更好的是,您可以将 .accessory 类型更新行替换为:

    tableView.reloadRows(at: [indexPath], with: .none)
    

    所以方法看起来像这样:

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
    
        if let cell = tableView.cellForRow(at: indexPath) {        
            let isCellSelected = !(model[indexPath.row].isCellSelected)
            model[indexPath.row].isCellSelected = isCellSelected
            tableView.reloadRows(at: [indexPath], with: .none)
        }
    }
    

    请注意,我们更新model,然后重新加载将导致cellForRowAt 被调用的单元格,并且该方法会根据模型进行正确的配置。

    更一般地说,我建议使用一些StateController 对象来保持每个单元格/行的状态并注意toggling

    我不久前写了一篇完整的文章,它完全可以满足您的需要,并且还展示了许多最佳实践:

    https://idevtv.com/how-to-display-a-list-of-items-in-ios-using-table-views/

    【讨论】:

    • 我会试试这个...你试过了吗?这是否适用于基于单选和多选的表格(在开始时已经检查了一些单元格)?顺便说一句,我提供了我所有的代码,所以如果你有时间,你可以自己重现问题。
    • 是的,这是生产代码,它可以工作。我的代码允许您选择许多单元格。我的表实际上已经将 allowedMultipleSelection 设置为 false。在选择时,它会取消选择具有默认动画的行,但它会跟踪它,根据需要设置/隐藏复选标记,并在 State Controller 类的数组内设置真/假标志。
    【解决方案5】:

    @Whirlwind,嗨,如果您只需要在 tableView 内显示选择,这并不复杂,我在您的模型更新后提供我的答案。

    这可以通过仅在didSelectRowAt 中维护isCellSelected 属性来完成。

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        model[indexPath.row].isCellSelected = model[indexPath.row].isCellSelected ? false : true
        tableView.reloadData()
    }
    

    这里是cellForRowAt,你也可以在这里多修改几行。

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let data = model[indexPath.row]
        let identifier = "kNormalID"
        let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
        cell.textLabel?.text = data.title
        cell.detailTextLabel?.text = data.subtitle
        cell.accessoryType = data.isCellSelected ? .checkmark : .none
        return cell
    }
    

    希望我能做到最好。如果我错过了什么,请告诉我。

    【讨论】:

      【解决方案6】:

      你为什么不试试关注!

           func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
      
          if let cell = tableView.cellForRow(at: indexPath) {
              cell.accessoryType = .none
              model[indexPath.row].isCellSelected = false
          tableView.reloadRows(at: [indexPath], with: <UITableViewRowAnimation>)
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-06
        • 2014-02-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多