如何使自定义单元格中的 UILabel 显示保存在 UITableView 中的更改

How to make UILabel from custom cell display saved changes in UITableView

我是 Swift 的初学者。我已经用尽了所有的尝试和错误,需要帮助!!

我正在使用 UITableView 创建一个记分牌项目,该项目带有一个包含 UILabel 和 UIButton 的自定义单元格。按下按钮后,UILabel 递增 1 以模拟玩家的一个点。我无法在 UILabel 中保存该点,因此每次我打开该应用程序时,该播放器的点仍然存在。我试过使用 UserDefaults、结构和委托,但没有任何运气......我可能做错了。我只需要知道正确的方法是什么。

注意:我能够从 UIAlertController 中成功保存播放器名称,这样当我打开应用程序时名称仍然存在,除非我删除它们,但没有运气保存每个名称本身的分数,它们仍然是“0”。

当我关闭然后打开应用程序时它应该是这样的,但只有当应用程序打开时它才会这样:

Scoreboard UITableView - Screenshot

这是 ViewController 代码:

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    var items = [String]()

    @IBOutlet weak var listTableView: UITableView!
    @IBAction func addItem(_ sender: AnyObject) {
        alert()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        listTableView.dataSource = self
        self.items = UserDefaults.standard.stringArray(forKey:"items")  ?? [String]()
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! PointsCell

        cell.textLabel?.text = items[indexPath.row]

        return cell
    }


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }

    func saveData() {
        UserDefaults.standard.set(items, forKey: "items")
    }

    func alert(){
        let alert = UIAlertController(title: "Add Player", message: "", preferredStyle: .alert)
        alert.addTextField{
            (textfield) in
            textfield.placeholder = " Enter Player Name "

        }
        let add = UIAlertAction(title: "Add", style: .default)
            {

            (action) in guard let textfield = alert.textFields?.first else {return}

            if let newText = textfield.text
            {
                self.items.append(newText)
                self.saveData()
                let indexPath = IndexPath(row: self.items.count - 1, section: 0)
                self.listTableView.insertRows(at: [indexPath], with: .automatic)
            }
        }

        let cancel = UIAlertAction(title: "Cancel", style: .cancel) {
            (alert) in

        }

        alert.addAction(add)
        alert.addAction(cancel)

        present(alert, animated: true, completion: nil)

    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        items.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .automatic)
        saveData()

    }

}

这是我的自定义单元格代码,名为 PointsCell:

import UIKit

class PointsCell: UITableViewCell {

    var winScore = 0
    @IBOutlet weak var scoreUILabel: UILabel!

   @IBAction func pointButtonPressed(_ sender: Any) {
        winScore += 1
        scoreUILabel.text = "\(winScore)"

    }

}

由于单元格已出队并且将数据存储在单元格子类中将不起作用,请选择不同的策略。您的 items 数组应包含有关 的信息,因此要实现此目的,您需要自定义模型。


首先,制作自定义模型的数组。该模型将保存标题和项目的点数

struct Item {
    var title: String
    var points: Int

    init(title: String, points: Int = 0) {
        self.title = title
        self.points = points
    }
}

然后将数组类型更改为 Item

的数组
var items = [Item]()

现在 cellForRowAt 根据索引项目设置出口

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! PointsCell
    let item = items[indexPath.row]
    cell.textLabel?.text = item.title
    cell.scoreUILabel.text = "\(item.points)"
    return cell
}

但是现在,如何处理用户按下单元格中的按钮以及如何增加分数?

一个解决方案是在 UITableViewCell 子类中创建闭包变量,当按钮被按下时,这个闭包会让控制器知道按钮已被按下

class PointsCell: UITableViewCell {

    var buttonPressed: (()->Void)?

    @IBOutlet weak var scoreUILabel: UILabel!

    @IBAction func pointButtonPressed(_ sender: Any) {
        buttonPressed?()
    }

}

然后在cellForRowAt里面分配这个参数,说如果按钮被按下,增加这个项目的points并重新加载这一行

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! PointsCell
    let item = items[indexPath.row]
    cell.textLabel?.text = item.title
    cell.scoreUILabel.text = "\(item.points)"

    cell.buttonPressed = { // this gets called when `buttonPressed?()` is called from cell class
        self.items[indexPath.row].points += 1
        tableView.reloadRows(at: [indexPath], with: .automatic)
        saveData() // optional
    }

    return cell
}

请注意,使用此策略,您还需要将追加新元素更改为追加 Item(顺便说一句,您可以安全地 force-unwrap text of UITextField 因为这个 属性 永远不会 nil)

self.items.append(Item(title: textfield.text!))
self.saveData()
let indexPath = IndexPath(row: self.items.count - 1, section: 0)
self.listTableView.insertRows(at: [indexPath], with: .automatic)

另请注意,要将自定义模型保存到 UserDefaults,您需要对自定义模型进行编码和解码,因此您需要将 Codable 实现到您的结构

struct Item: Codable {
    //...
}

然后为了保存使用 JSONEncoder:

func saveData() {
    do {
        let encoded = try JSONEncoder().encode(items)
        UserDefaults.standard.set(encoded, forKey: "items")
    } catch { print(error) }
}

为了检索,JSONDecoder:

override func viewDidLoad() {
    super.viewDidLoad()
    listTableView.dataSource = self

    do {
        if let data = UserDefaults.standard.data(forKey:"items") {
            items = try JSONDecoder().decode([Item].self, from: data)
        }
    } catch { print(error) }
}

未来的最后一个注意事项:UserDefaults 不是为存储大量数据而设计的,在未来的编程生活中,您肯定会使用一些数据库,例如 Realm 或本机 CoreData.

祝你好运! ;)