UITableViewCell 无意中在不需要的行上放置了复选标记
Posted
技术标签:
【中文标题】UITableViewCell 无意中在不需要的行上放置了复选标记【英文标题】:UITableViewCell unintentionally placing checkmark on unwanted rows 【发布时间】:2018-02-13 02:19:41 【问题描述】:我正在制作一个音乐流派选择应用程序,当我去我的桌子选择流派时,我选择了一行,它从我的选择中选择了大约 10 左右的随机行。
我的选择代码是:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
let genresFromLibrary = genrequery.collections
let rowitem = genresFromLibrary![indexPath.row].representativeItem
print(rowitem?.value(forProperty: MPMediaItemPropertyGenre) as! String
)
if let cell = tableView.cellForRow(at: indexPath)
cell.accessoryType = .checkmark
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath)
if let cell = tableView.cellForRow(at: indexPath)
cell.accessoryType = .none
【问题讨论】:
【参考方案1】:在调用cellForRowAtIndexPath
时,默认情况下会重用单元格。当您不跟踪已选择的 indexPaths 时,这会导致单元格包含错误的数据。您需要跟踪当前选择的索引路径,以便在表格视图中显示适当的附件类型。
一种方法是在 UITableViewController 中有一个属性,它只存储所选单元格的索引路径。可以是数组,也可以是集合。
var selectedIndexPaths = Set<IndexPath>()
当您在didSelectRowAt
上选择一行时,在selectedIndexPaths
中添加或删除单元格,具体取决于索引路径是否已在数组中:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
if selectedIndexPaths.contains(indexPath)
// The index path is already in the array, so remove it.
selectedIndexPaths.remove(indexPathIndex)
else
// The index path is not part of the array
selectedIndexPaths.append(indexPath)
// Show the changes in the selected cell (otherwise you wouldn't see the checkmark or lack thereof until cellForRowAt got called again for this cell).
tableView.reloadRows(at: [indexPath], with: .none)
一旦你有了这个,在你的 cellForRowAtIndexPath
上,检查 indexPath 是否在 selectedIndexPaths
数组中以选择 accessoryType
。
if selectedIndexPaths.contains(indexPath)
// Cell is selected
cell.accessoryType = .checkmark
else
cell.accessoryType = .none
这应该可以解决每 10 个左右检查一次看似随机的单元格的问题(这不是随机的,只是带有复选标记的单元格正在被重复使用)。
【讨论】:
在didSelectRowAt
中,更新selectedIndexPaths
后简单地重新加载indexPath
处的行可能比获取和更新单元格更好。
@rmaddy 好点。我忘了reloadRows。我会更新我的答案来改变这一点。
如果您将selectedIndexPaths
设置为Set
而不是Array
,您的代码也会更加高效。然后删除索引路径会更有效。
是的,我提到它可以是数组或集合,通常我在这些示例中使用数组,因为大多数新手可能更习惯使用它们。无论如何,我已经更改了它,因为我已经完成了reloadRows
更改。感谢您指出这一切,@rmaddy!
现在您已经将其设置为一组,您需要重做删除索引路径的代码。您不再需要获取索引。【参考方案2】:
因为cellForRow
返回您生成的缓存单元格。当滚动出屏幕时,单元格的顺序会改变并且单元格会被重复使用。所以它似乎是“随机选择的”。
不要使用cellForRow
,而是记录选择数据。
这里的代码在单视图游乐场中工作。
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController, UITableViewDataSource, UITableViewDelegate
let tableView = UITableView()
var selection: [IndexPath: Bool] = [:]
override func viewDidLoad()
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.tableFooterView = UIView()
view.addSubview(tableView)
override func viewDidLayoutSubviews()
super.viewDidLayoutSubviews()
tableView.frame = self.view.bounds
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "c")
if let sc = cell
sc.accessoryType = .none
let isSelected = selection[indexPath] ?? false
sc.accessoryType = isSelected ? .checkmark : .none
return sc
return UITableViewCell(style: .default, reuseIdentifier: "c")
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
cell.textLabel?.text = NSNumber(value: indexPath.row).stringValue
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
selection[indexPath] = true
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
func numberOfSections(in tableView: UITableView) -> Int
return 1
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return 30
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
【讨论】:
为什么accessoryType
在cellForRowAt
中设置了两次?
这是我第一次看到这样做。我觉得这会变得很复杂,因为您必须一直跟踪所有索引路径及其各自的布尔值,特别是如果您的表视图控制器非常动态(单元格插入、删除、移动等)。以上是关于UITableViewCell 无意中在不需要的行上放置了复选标记的主要内容,如果未能解决你的问题,请参考以下文章
在 ViewSwitcher 中在不确定的 ProgressBar 和 Image 之间切换
重复的 UItableViewCell,UITableView 的行