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

时间:2019-02-04 14:40:23

标签: ios swift uitableview userdefaults

我是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)"

    }

}

1 个答案:

答案 0 :(得分:2)

由于单元已出队,并且无法在单元子类中存储数据,请选择其他策略。您的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(顺便说一句,您可以安全地强制展开text中的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之类的数据库。

祝你好运! ;)