如何在Swift中使用NSCoding存储从ViewController生成的值

时间:2016-02-13 15:32:05

标签: ios swift

我有来自“ScoreHistory.swift”的以下变量:

// ScoreHistory.swift

class ScoreHistory: NSObject, NSCoding {

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

在我的QuizViewController中,我有以下变量:

// QuizViewController.swift

var datePlayed = NSDate()
var totalScore = 0
var totalAnswered = 0
var totalDuration = 0
var gameStatus: String?

每次游戏会话结束后,用户可能会收到以下信息:(显示在“ScoreViewController”中)

// Example data

datePlayed: (date today)
totalScore: 10
totalAnswered: 15
totalDuration: 1 min. 3 sec.
gameStatus: Exam Finished

我需要从QuizViewController获取上述数据以存储到“ScoreHistory.swift” 我需要在ScoreViewController.swift中显示数据:

// ScoreViewController.swift

class ScoreViewController: UIViewController, UINavigationControllerDelegate {

// in viewDidLoad()

if let score = score {

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

如何将生成的数据从QuizViewContoller存储到ScoreHistory?以及如何将所述数据显示给ScoreViewController?

我的ScoreHistory看起来像这样:

class ScoreHistory: NSObject, NSCoding {

// MARK: Properties

var datePlayed: NSDate
var totalScore: Int
var totalAnswered: Int
var totalDuration: Int
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: NSDate, totalScore: Int, totalAnswered: Int, totalDuration: Int, gameStatus: String) {
    // Initialize stored properties.

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


    super.init()

    // Initialization should fail if there is no name or if the rating is negative.
    if gameStatus.isEmpty {
        return nil
    }
}

// MARK: NSCoding

func encodeWithCoder(aCoder: NSCoder) {

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

required convenience init?(coder aDecoder: NSCoder) {

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

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

}

1 个答案:

答案 0 :(得分:0)

我不确定我是否理解你的问题。你的问题是:

  1. 如何将得分历史记录存储到文件中。
  2. 如何将更改的信息从一个视图控制器更改为另一个视图控制器。
  3. 我无法根据您提供的代码弄明白。

    回答1。 您正确地在ScoreHistory类中实现了NSCoding方法。我在这里假设这是你的模型类,必须保存整个游戏中收集的数据。 要将数据保存到文件,只需使用NSKeyedArchiver对象即可。代码示例是:

        func saveStatisticsToDisk() {
        let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(savedScore, toFile: ScoreHistory.scoreStore.path!)
    
        if !isSuccessfulSave {
            //Set the alert properties.
            let title = "Warning"
            let message = "Unable to save the scores"
            let animationFlag = true
    
            //configure the alert.
            let alertMessage = UIAlertController.init(title: title, message: message, preferredStyle: .Alert)
            let okAction = UIAlertAction(title: "Ok", style: .Default) {
                (action) -> Void in
                self.presentingViewController?.dismissViewControllerAnimated(animationFlag, completion: nil)
            }
            alertMessage.addAction(okAction)
    
            //Present the alert.
            self.presentViewController(alertMessage, animated: animationFlag, completion: nil)
        }
    }
    

    回答2。 这更复杂,因为您希望将数据从一个视图控制器获取到另一个视图控制器。编码最佳实践告诉您应该使用NSNotifications将更改从一个视图控制器发送到另一个视图控制器。这对于防止强参考周期是必要的。

    这意味着在QuizViewController中你有一个ScoreHistory类的实例。我们称之为“quizScoreHistory”。在测验期间,实例quizScoreHistory更新,当玩家完成一个级别,或者如你所说:“完成一个会话后”,ScoreViewController需要知道“quizScoreHistory”的详细信息,因此它可以显示信息给玩家。

    我在这里向您展示如何使用字典userInfo为标准NSNotification执行此操作。 在QuizViewController类中,您可以使用以下代码在默认通知中心中发送通知:

        func notifyOnTrackChanges() {
    //This function makes a dictionary with the key value pairs that reflect your ScoreHistory class.
    //The a notification is posted under the name "scoreDidChange" posting the dictionary as userInfo. 
    //The ScoreOverviewController will pick this up and store the values in the score to be displayed.
    
        let userInfo : [String: String] = ["date played": dateformatter.stringFromDate((self.quizScoreHistory.datePlayed)!), "total score": String((self.quizScoreHistory.totalScore)!), "total answered": String((self.quizScoreHistory.totalAnswered)!), "total duration": String((self.quizScoreHistory.gameStatus)!), "game status": String((self.quizScoreHistory.gameStatus)!)]
        let trackDetailNotificationCenter = NSNotificationCenter.defaultCenter()
        trackDetailNotificationCenter.postNotificationName("scoreDidChange", object: self, userInfo: userInfo)
    }
    

    我把userInfo作为字符串放在字典中,因为你只想在ScoreViewController中显示它们并理解你提供的代码,你只需要将得分历史作为字符串显示在单元格中。

    现在您想要在ScoreViewController中接收通知。这可以在viewDidLoad,viewWillAppear中实现以下代码,或者更好地在ScoreHistory实例中将其写为setter。

            //This is the observer created to get the changes back from the QuizViewController.
        let notificationCenter = NSNotificationCenter.defaultCenter()
        let operationsQueue = NSOperationQueue.mainQueue()
        self.scoreChangedObserver = notificationCenter.addObserverForName("scoreDidChange", object: nil, queue: operationsQueue, usingBlock: { (notification: NSNotification!) -> Void in
    
            let score.datePlayed = notification.userInfo!["date played"]!
            let score.totalScore = notification.userInfo!["total score"]!
            let score.totalAnswered = notification.userInfo!["total answered"]!
            let score.totalDuration = notification.userInfo!["total duration"]!
            let score.gameStatus = notification.userInfo!["gameStatus"]!
    
            self.displayScore()
        })
    

    您的显示分数可能与您在问题中提供的一样,假设分数是该方法的属性:

    func displayScore() {
        if let score = score {
    
            navigationItem.title = score.datePlayed
            datePlayedLabel.text = score.datePlayed
            totalScoreLabel.text   = score.totalScore
            totalAnsweredLabel.text   = score.totalAnswered
            totalDurationLabel.text   = score.totalDuration
            gameStatusLabel.text   = score.gameStatus
        }
    }
    

    我希望这会有所帮助。