#ios #swift #uitableview #nsuserdefaults
#iOS #swift #uitableview #nsuserdefaults
Вопрос:
Я использую NSUserDefaults
для постоянного хранения в своем приложении. Это игра, в которой после окончания игры счет, а также дата и имя (введенные пользователем) должны быть сохранены, а затем показаны в виде таблицы. Мой код до сих пор:
import UIKit
class LeaderboardVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
var finishedGame = 0
var gameScore:Int! = 0
var name:String!
var date:String!
var score:Int!
var scoreData = [NSArray]()
var defaults = UserDefaults.standard
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
if finishedGame == 1{
saveNew()
}
self.tableView.delegate = self
self.tableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = false
self.navigationItem.hidesBackButton = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func saveNew() {
let enterNameAlert = UIAlertController(title: "Please Enter Your Name", message: "This will be used to place you in the leaderboards", preferredStyle: .alert)
enterNameAlert.addTextField { (textField:UITextField) -> Void in
textField.placeholder = "Name"
textField.autocapitalizationType = UITextAutocapitalizationType.words
textField.autocorrectionType = UITextAutocorrectionType.no
textField.clearsOnBeginEditing = true
textField.clearsOnInsertion = true
textField.clearButtonMode = UITextFieldViewMode.always
textField.keyboardType = UIKeyboardType.default
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (action:UIAlertAction) in
let currentTime = Date()
let timeFormatter = DateFormatter()
timeFormatter.locale = Locale.current
timeFormatter.dateFormat = "HH:mm dd/MM/yy"
let convertedTime = timeFormatter.string(from: currentTime) //date
let enteredName = enterNameAlert.textFields?.first?.text //name
// set object of date,name and score here
}
enterNameAlert.addAction(cancelAction)
enterNameAlert.addAction(confirmAction)
self.present(enterNameAlert, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! LeaderBoardCell
cell.dateLabel?.text =
cell.dateLabel.adjustsFontSizeToFitWidth = true
cell.scoreLabel?.text =
cell.scoreLabel.adjustsFontSizeToFitWidth = true
cell.nameLabel?.text =
cell.nameLabel.adjustsFontSizeToFitWidth = true
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return scoreData.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
В ячейке есть 3 метки, по одной для каждого из 3 значений, которые должны отображаться. Как мне установить эти значения, используя NSUserDefaults
(они должны быть вместе, например, оценка: 500 идет с именем: John и так далее)?
РЕДАКТИРОВАТЬ — НОВЫЙ КОД
import UIKit
таблица лидеров классаvc: UIViewController, UITableViewDataSource, UITableViewDelegate {
var finishedGame = 0
var gameScore:Int! = 0
var defaults = UserDefaults.standard
var newUserArray = NSMutableArray()
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
if finishedGame == 1{
saveNew()
}
self.tableView.delegate = self
self.tableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = false
self.navigationItem.hidesBackButton = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func saveNew() {
let enterNameAlert = UIAlertController(title: "Please Enter Your Name", message: "This will be used to place you in the leaderboards", preferredStyle: .alert)
enterNameAlert.addTextField { (textField:UITextField) -> Void in
textField.placeholder = "Name"
textField.autocapitalizationType = UITextAutocapitalizationType.words
textField.autocorrectionType = UITextAutocorrectionType.no
textField.clearsOnBeginEditing = true
textField.clearsOnInsertion = true
textField.clearButtonMode = UITextFieldViewMode.always
textField.keyboardType = UIKeyboardType.default
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (action:UIAlertAction) in
let currentTime = Date()
let timeFormatter = DateFormatter()
timeFormatter.locale = Locale.current
timeFormatter.dateFormat = "HH:mm dd/MM/yy"
let convertedTime = timeFormatter.string(from: currentTime)
let enteredName = enterNameAlert.textFields?.first?.text
let newUserData = self.defaults.object(forKey: "UserData") as! NSArray
let newUserArray = NSMutableArray(array: newUserData)
let newUserRecord = [
"Name" : enteredName!,
"Score" : String(self.gameScore),
"Date" : convertedTime
] as [String : String]
newUserArray.add(newUserRecord)
self.defaults.set(newUserArray, forKey: "UserData")
self.defaults.synchronize()
print(newUserArray)
print("hello")
print(self.defaults)
}
enterNameAlert.addAction(cancelAction)
enterNameAlert.addAction(confirmAction)
self.present(enterNameAlert, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! LeaderBoardCell
let userData = defaults.object(forKey: "UserData") as! NSArray
cell.dateLabel?.text = "Date: (((userData.object(at: indexPath.row) as! [String:Any])["Date"] as! String))"
cell.dateLabel.adjustsFontSizeToFitWidth = true
cell.scoreLabel?.text = "Score: (((userData.object(at: indexPath.row) as! [String:Any])["Score"] as! String))"
cell.scoreLabel.adjustsFontSizeToFitWidth = true
cell.nameLabel?.text = "Name: (((userData.object(at: indexPath.row) as! [String:Any])["Name"] as! String))"
cell.nameLabel.adjustsFontSizeToFitWidth = true
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return newUserArray.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
Комментарии:
1. Если у вас несколько пользователей, я предлагаю вам сохранить данные в Core Data, иначе используйте объект Dictionary и сохраните в UserDefaults
2. Я пытался использовать Core Data, но в этом случае это не сработало для меня, поэтому я хочу использовать NSUserDefaults. Как я могу это сделать в NSUserDefaults?
3. Сколько пользователей в игре. Поскольку UserDefaults будет работать только для одного пользователя, и будет сохранен один объект
4. Игра будет воспроизводиться только на 1 устройстве и не имеет никаких онлайн-функций. Все, что мне нужно, это после каждой игры счет, имя и дата сохраняются и отображаются в виде таблицы
5. Можете ли вы поделиться своей структурой массива ScoreData, поскольку это будет ваш источник данных. scoreData состоит из чего?
Ответ №1:
Вы можете установить для словаря значение UserDefaults (это для 3 значений одновременно). Чтобы создать таблицу лидеров, вам нужно будет сохранить эти словари в массиве.
let scoreDict: [String : Any] = ["name": "John", "score": 500, "date": NSDate()]
let defaults = UserDefaults.standard
let scoresKey = "scores"
var currentScores: [Any] = defaults.array(forKey: scoresKey) ?? []
currentScores.append(scoreDict)
defaults.set(currentScores, forKey: scoresKey)
defaults.synchronize()
Ответ №2:
Я не смог получить столько, но я только что обнаружил, что вам нужно добавить некоторые элементы в ваш scoreData
для источника данных tableview
Здесь вы можете добавить полный объект в UserDefaults
//Make your scoreData as MutableArray
var scoreData = NSMutableArray()
// Record of a user
let user = [
"name" : "John",
"score" : 10,
"id" : 20
] as [String : Any]
scoreData.add(user)
defaults.set(scoreData, forKey: "UserData")
Как их извлечь
let userData = defaults.object(forKey: "UserData") as! NSArray
print((userData.object(at: 0) as! [String:Any])["name"] as! String)
print((userData.object(at: 0) as! [String:Any])["score"] as! Int)
print((userData.object(at: 0) as! [String:Any])["id"] as! Int)
Я напечатал их на консоли и только 0-й индекс. Вы можете распечатать их на своей этикетке с indexPath.row
элементами.
Редактировать
Чтобы добавить новую запись, сначала извлеките старые данные из UserDefaults
let newUserData = defaults.object(forKey: "UserData") as! NSArray
Затем преобразуйте его в изменяемый массив, потому что мы хотим добавить новую запись пользователя. Я мог бы сделать это во время выборки, но при выборке из UserDefaults
него выдается неизменяемый объект, независимо от того, к какому типу мы вводим приведение NSMutableArray
. Итак, преобразуйте объект newUserData
в изменяемый
let newUserMutableArray = NSMutableArray(array: newUserData)
Добавьте новую запись
// New record
let newUserRecord = [
"name" : "Rajan",
"score" : 20,
"id" : 30
] as [String : Any]
newUserMutableArray.add(newUserRecord)
Сохраните его снова
defaults.set(newUserMutableArray, forKey: "UserData")
И снова выборка, как указано выше. Теперь выборка NSArray
будет содержать два элемента.
В этом случае я по-прежнему предпочитаю использовать core data, поскольку обработка станет намного проще.
Редактировать
После выборки
scoreDataArray = defaults.object(forKey: "UserData") as! NSArray
Перезагрузите таблицу
self.yourTableView.reloadData()
Ваши методы источника данных в табличном представлении будут такими
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return scoreDataArray.count
}
В вашем cellForRowAt
методе источника данных
cell.scoreLabel?.text = scoreDataArray.object(at: indexPath.row) as! [String:Any])["score"] as! String
Комментарии:
1. Спасибо. Как бы мне сделать так, чтобы я мог печатать каждый доступный индекс? Просто перебирать?
2. Кажется, все работает нормально, чтобы добавить объект, однако я попытался добавить часть выборки вашего кода в метод cellForRow, однако он не отображается. Я думаю, мне нужно сначала выполнить выборку, как метод viewWillAppear, а затем использовать indexPath.row, как вы сказали. Как вы рекомендуете мне это сделать?
3. Сначала заполните полный массив данных о результатах и сохраните его. Затем позже извлеките его, вызвав некоторую функцию, создайте источник данных соответствующим образом и перезагрузите таблицу. В этом случае userData должен быть источником данных для таблицы
4. Хорошо, спасибо, еще один вопрос, будет ли этот метод работать для более чем одной записи?
5. @John_Sm1 да, так и будет. См. Редактирование в ответе. Но я все еще предпочитаю основные данные в этом случае