如何在Swift中使用NSCoding从ViewController保存数据

时间:2016-02-15 09:48:56

标签: ios swift nscoding

我需要使用NSCoding将数据从受控的ViewController(“ScoreView.swift”)保存到“ScoreHistory.swift”。我试过,但数据没有显示在“ScoreTableViewController.swift”中。我错过了什么?

我有这个ScoreView.swift,它有以下代码:(请注意,这是一个“segued”视图,其中数据已从另一个ViewController传递)

class ScoreView: UIViewController {

var dateToday = NSDate()

var score: ScoreHistory?

var numberofquestions:String = ""
var scorepassed:String = ""
var scorepercentpassed:String = ""
var scoreremarkspassed:String = ""
var totalduration:String!
var incorrectanswerspassed:String = ""
var skippedquestionspassed:String = ""


override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.

    datePlayedLabel.text = dateToday.description       
    totalScoreLabel.text = scorepassed    
    scorePercentage.text = scorepercentpassed
    totalAnsweredLabel.text = numberofquestions
    totalDurationLabel.text = totalduration
    gameStatusLabel.text = "Exam Finished"

    // NSCoding

    if let score = score {

        datePlayedLabel.text = score.datePlayed
        totalScoreLabel.text   = score.totalScore
        totalAnsweredLabel.text   = score.totalAnswered
        totalDurationLabel.text   = score.totalDuration
        gameStatusLabel.text   = score.gameStatus
    }


}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

    if backMenu === sender {

        let datePlayed = datePlayedLabel.text ?? ""
        let totalScore = totalScoreLabel.text ?? ""
        let totalAnswered = totalAnsweredLabel.text ?? ""
        let totalDuration  = totalDurationLabel.text ?? ""
        let gameStatus = gameStatusLabel.text ?? ""

        // Set the score to be passed to ScoreTableViewController after the unwind segue.

        score = ScoreHistory(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)
    }

    NSKeyedArchiver.archiveRootObject(score!, toFile: ScoreHistory.ArchiveURL.path!)
}


override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(true)

    datePlayedLabel.text = dateToday.description       
    totalScoreLabel.text = scorepassed    
    scorePercentage.text = scorepercentpassed
    totalAnsweredLabel.text = numberofquestions
    totalDurationLabel.text = totalduration
    gameStatusLabel.text = "Exam Finished"
}

// Labels

}
}

我有ScoreHistory.swift,它有以下代码:

class ScoreHistory: NSObject, NSCoding {

// MARK: Properties

var datePlayed: String
var totalScore: String
var totalAnswered: String
var totalDuration: String
var gameStatus: String

// MARK: Archiving Paths

static let DocumentsDirectory = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("scores")

// MARK: Types

struct PropertyKey {
    static let datePlayedKey = "datePlayed"
    static let totalScoreKey = "totalScore"
    static let totalAnsweredKey = "totalAnswered"
    static let totalDurationKey = "totalDuration"
    static let gameStatusKey = "gameStatus"
}

// MARK: Initialization

init?(datePlayed: String, totalScore: String, totalAnswered: String, totalDuration: String, gameStatus: String) {
    // Initialize stored properties.

    self.datePlayed = datePlayed
    self.totalScore = totalScore
    self.totalAnswered = totalAnswered
    self.totalDuration = totalDuration
    self.gameStatus = gameStatus

    super.init()

}

// MARK: NSCoding

func encodeWithCoder(aCoder: NSCoder) {

    aCoder.encodeObject(datePlayed, forKey: PropertyKey.datePlayedKey)
    aCoder.encodeObject(totalScore, forKey: PropertyKey.totalScoreKey)
    aCoder.encodeObject(totalAnswered, forKey: PropertyKey.totalAnsweredKey)
    aCoder.encodeObject(totalDuration, forKey: PropertyKey.totalDurationKey)
    aCoder.encodeObject(gameStatus, forKey: PropertyKey.gameStatusKey)
}

required convenience init?(coder aDecoder: NSCoder) {

    let datePlayed = aDecoder.decodeObjectForKey(PropertyKey.datePlayedKey) as! String
    let totalScore = aDecoder.decodeObjectForKey(PropertyKey.totalScoreKey) as! String
    let totalAnswered = aDecoder.decodeObjectForKey(PropertyKey.totalAnsweredKey) as! String
    let totalDuration = aDecoder.decodeObjectForKey(PropertyKey.totalDurationKey) as! String
    let gameStatus = aDecoder.decodeObjectForKey(PropertyKey.gameStatusKey) as! String

    // Must call designated initializer.
    self.init(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)
}

}

以下是ScoreTableViewController.swift的完整代码:

class ScoreTableViewController: UITableViewController {
// MARK: Properties

var scores = [ScoreHistory]()

var dateToday = NSDate()

override func viewDidLoad() {
    super.viewDidLoad()

    // Load any saved scores, otherwise load sample data.
    if let savedScores = loadScores() {
        scores += savedScores
    } else {
        // Load the sample data.
        loadSampleScores()
    }
}

func loadSampleScores() {

    let score1 = ScoreHistory(datePlayed: dateToday.description, totalScore: "0", totalAnswered: "0", totalDuration: "0", gameStatus: "started")!

    scores += [score1]
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return scores.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    // Table view cells are reused and should be dequeued using a cell identifier.

    let cellIdentifier = "ScoreHistoryTableViewCell"
    let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ScoreHistoryTableViewCell

    // Fetches the appropriate note for the data source layout.
    let score = scores[indexPath.row]

    cell.datePlayedLabel.text = score.datePlayed
    cell.totalScoreLabel.text = score.datePlayed
    cell.totalScoreLabel.text   = score.totalScore
    cell.totalAnsweredLabel.text   = score.totalAnswered
    cell.totalDurationLabel.text   = score.totalDuration
    cell.gameStatusLabel.text   = score.gameStatus

    return cell
}

// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    // Return false if you do not want the specified item to be editable.
    return true
}


// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {

    if editingStyle == .Delete {

        // Delete the row from the data source
        scores.removeAtIndex(indexPath.row)
        saveScores()

        tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
    }
}

// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "ShowDetail" {
        let scoreDetailViewController = segue.destinationViewController as! ScoreViewController

        // Get the cell that generated this segue.
        if let selectedScoreCell = sender as? ScoreHistoryTableViewCell {
            let indexPath = tableView.indexPathForCell(selectedScoreCell)!
            let selectedScore = scores[indexPath.row]
            scoreDetailViewController.score = selectedScore
        }
    }
}

// MARK: NSCoding

func saveScores() {
    let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(scores, toFile: ScoreHistory.ArchiveURL.path!)
    if !isSuccessfulSave {
        print("Failed to save scores...")
    }
}

func loadScores() -> [ScoreHistory]? {
    return NSKeyedUnarchiver.unarchiveObjectWithFile(ScoreHistory.ArchiveURL.path!) as? [ScoreHistory]
}

@IBAction func unwindToScoreList(sender: UIStoryboardSegue) {
    if let sourceViewController = sender.sourceViewController as? ScoreViewController, score = sourceViewController.score {

        if let selectedIndexPath = tableView.indexPathForSelectedRow {
            // Update an existing note.
            scores[selectedIndexPath.row] = score
            tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)

            // Add a new score.

        let newIndexPath = NSIndexPath(forRow: scores.count, inSection: 0)
            scores.append(score)
            tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)

            saveScores()
        }
    }
}

}

目标:我的目标是在用户完成测验游戏时记录/存储来自“ScoreView.swift”的所有会话数据。 每次测验游戏后都会显示“ScoreView”,我打算将每个测验结果记录在“ScoreHistory.swift”中。我该怎么做?

2 个答案:

答案 0 :(得分:0)

您的loadScores函数正在加载已归档的分数数组:

func loadScores() -> [ScoreHistory]? {
    return NSKeyedUnarchiver.unarchiveObjectWithFile(ScoreHistory.ArchiveURL.path!) as? [ScoreHistory]
}

在您的segue中,您只会归档一个分数。您无法归档ScoreHistory实例并希望取消归档ScoreHistory数组。你现在的位置:

    score = ScoreHistory(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)

您需要将其更改为:

    var scores = loadScores() ?? []
    score = ScoreHistory(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)
    scores.append(score)
    saveScores(scores)

其中loadScoressaveScoresScoreTableViewController中的代码相同,但我添加了得分以保存为参数,因为此代码会创建一个本地变量。

更新:现在已经很晚了,我没有给予足够的重视。你需要处理loadScores返回nil,当然scores应该是var而不是let,否则你将无法添加它。通过这些更改,scores不再是可选的,因此您无需打开它。

答案 1 :(得分:0)

最简单的解决方案是将UITextField个实例中已更改的值保存回score中的ScoreView个实例(为什么score可选,因为您总是通过一个非可选的score实例??)并展开segue。 然后,数组保存在unwindToScoreList

的方法ScoreTableViewController
  override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

    if backMenu === sender {
      score?.datePlayed = datePlayedLabel.text ?? ""
      score?.totalScore = totalScoreLabel.text ?? ""
      score?.totalAnswered = totalAnsweredLabel.text ?? ""
      score?.totalDuration  = totalDurationLabel.text ?? ""
      score?.gameStatus = gameStatusLabel.text ?? ""
    }
  }

ScoreView无法存档!