有一个表格视图显示文章列表:文章。 文章是一个结构对象,文章列表是从api响应初始化的。
我们需要添加一项功能,当用户点击一个单元格时,它会更改为选定状态。为了表明选择,我在选定的项目单元格中添加了一个复选标记。
cell.accessoryType = .checkmark
出现问题。由于细胞被回收,每次选择一个选定的文章细胞时,我们需要存储选择的细胞。
struct article在整个应用中使用,因此不应只需添加isSelected字段。
struct Article {
let title: String
var isSelected: Bool // <- Not good. Article used over the app.
}
我使用的一些解决方案是使bool数组存储选择哪一行。
var selectedRow: Bool = {
return articles.map { return false }
}()
但这种方法似乎很奇怪,也容易受到细胞的侵入和删除。
有更好的解决方案吗?
import UIKit
struct Article {
let title: String
}
class SampleTableViewController: UITableViewController {
// MARK: - Properties
let articles: [Article] = {
var tempArticles: [Article] = []
for i in 0...30 {
tempArticles.append(Article(title: "Article at index:\(i)"))
}
return tempArticles
}()
lazy var selectedRow: [Bool] = {
return articles.map { _ in false }
}()
let cellId = "cell"
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return articles.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
cell.textLabel?.text = articles[indexPath.row].title
// Set selected state to a cell
cell.accessoryType = selectedRow[indexPath.row] ? .checkmark : .none
return cell
}
// MARK: - Table view delegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Update selection
// This isn't the best way to update selection but just to make the example simple.
selectedRow[indexPath.row] = !selectedRow[indexPath.row]
tableView.reloadData()
}
}
答案 0 :(得分:2)
解决此问题的一种方法:为表格查看它自己的模型,该模型可以保存列表的状态,包括选定的状态。
class ListItem {
let article: Article
var isSelected: Bool
}
然后从这些项目的数组中构建表格视图:
var listItems = [ListItem]()
使用包含文章的ListItems填充该数组,并将其用作数据源。
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let item = listItems[indexPath.row]
cell.textLabel?.text = item.article.title
cell.accessoryType = item.isSelected ? .checkmark : .none
return cell
}
最后在didSelectRowAtIndexPath
中,您只需设置所选列表项的新isSelected
值。
答案 1 :(得分:1)
我会使用Set<Article>
来跟踪您选择的文章。为此,您的Article
结构必须为Hashable
。只要结构的所有属性都是Hashable
,那么您需要做的就是添加Hashable
协议的一致性。
struct Article: Hashable {
let title: String
}
var selectedArticles = Set<Article>()
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let article = articles[indexPath.row]
cell.textLabel?.text = article.title
// Set selected state to a cell
cell.accessoryType = selectedArticles.contains(article) ? .checkmark : .none
return cell
}
// MARK: - Tabkle view delegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let article = article[indexPath.row]
if selectedArticles.contains[article] {
selectedArticles.remove(article)
} else {
selectedArticles.insert(article)
}
tableView.reloadRows(at: [indexPath], with: .none)
}
答案 2 :(得分:0)
我增强多利安·罗伊的答案使用泛型。
通过这种方式我们只需要声明一次,而不是每种类型的模型
struct SelectableListItem<T> {
let item: T
var isSelected: Bool
}
表视图实现
var listItems: [SelectableListItem<Article>] = []
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let listItem = listItems[indexPath.row]
cell.textLabel?.text = listItem.item.title
cell.accessoryType = listItem.isSelected ? .checkmark : .none
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Update data
listItems[indexPath.row].isSelected = !listItems[indexPath.row].isSelected
// Update selection
let cell = tableView.cellForRow(at: indexPath)
cell?.accessoryType = listItems[indexPath.row].isSelected ? .checkmark : .none
}