如何存储细胞的选择,需要最佳实践

时间:2018-06-08 07:02:01

标签: ios swift uitableview

情况

有一个表格视图显示文章列表:文章。 文章是一个结构对象,文章列表是从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()
    }
}

enter image description here

3 个答案:

答案 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
    }