如何在单元格渲染后检查表格视图上的复选标记图标(附件视图)?
Posted
技术标签:
【中文标题】如何在单元格渲染后检查表格视图上的复选标记图标(附件视图)?【英文标题】: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
【问题讨论】:
只显示您使用的模型结构,可以通过cellForRowAt
和didSelectRowAt
..完成。
@vaibhav 我添加了模型示例。
如果你还是卡住了,我把我的答案发过来看看。
【参考方案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 中调用它,因为我必须确保表格已经绘制了它的内容(因为如果我们尝试一些尚不存在的东西(单元格),这将失败)。不是最好的方法,但它解决了 single 和 multiple 选择与 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>)
【讨论】:
以上是关于如何在单元格渲染后检查表格视图上的复选标记图标(附件视图)?的主要内容,如果未能解决你的问题,请参考以下文章