如何使自定义单元格中的 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
.
祝你好运! ;)
我是 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
.
祝你好运! ;)