NSUser по умолчанию — сохранение 3 переменных вместе, а затем выборка

#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 да, так и будет. См. Редактирование в ответе. Но я все еще предпочитаю основные данные в этом случае