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

Posted

技术标签:

【中文标题】如何使自定义单元格中的 UILabel 显示保存在 UITableView 中的更改【英文标题】:How to make UILabel from custom cell display saved changes in UITableView 【发布时间】:2019-02-04 14:40:22 【问题描述】:

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

    


【问题讨论】:

请分享 saveData() 代码 好的,我找到了 saveData()。请在将值分配给 self.items 后重新加载 viewDidload() 中的数据,它将修复您的问题 【参考方案1】:

由于单元格已出列并且无法在单元格子类中存储数据,因此请选择不同的策略。您的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(顺便说一句,您可以安全地强制解开 UITextFieldtext,因为此属性永远不会是 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

顺便说一句,祝你好运! ;)

【讨论】:

这就像一个魅力!非常感谢您的详细解释!

以上是关于如何使自定义单元格中的 UILabel 显示保存在 UITableView 中的更改的主要内容,如果未能解决你的问题,请参考以下文章

如何禁用自定义单元格中的按钮?

自定义单元格中的 UILabel 不会动态增加高度

如何使用自定义表格视图单元格中识别的点击手势从 UILabel 呈现视图控制器?

在自定义单元格中调整 iOS 7.1 中的 UILabel 大小

自定义大小的表格视图单元格中的 UILabel 中的行数

为啥我的自定义表格单元格中的标签在单击行之前无法显示