Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/110.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 如何在Swift中使用NSCoding从ViewController保存数据_Ios_Swift_Nscoding - Fatal编程技术网

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

Ios 如何在Swift中使用NSCoding从ViewController保存数据,ios,swift,nscoding,Ios,Swift,Nscoding,我需要使用NSCoding将数据从segued ViewController(“ScoreView.swift”)保存到“ScoreHistory.swift”。我试过了,但是数据没有显示在“ScoreTableViewController.swift”中。我错过了什么 我有这个ScoreView.swift,它有以下代码:(请注意,这是一个“分段”视图,其中数据已从另一个ViewController传递) 我有ScoreHistory.swift,它有以下代码: class ScoreHist

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

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

我有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”中记录每个测验结果。我该怎么做?

您的
加载分数
功能正在加载存档的分数数组:

func loadScores() -> [ScoreHistory]? {
    return NSKeyedUnarchiver.unarchiveObjectWithFile(ScoreHistory.ArchiveURL.path!) as? [ScoreHistory]
}
在你的序列中,你只存档了一个分数。您不能存档
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)
其中
loadScores
saveScores
ScoreTableViewController
中的代码相同,尽管我已经添加了分数以另存为参数,因为该代码创建了一个局部变量


更新:很晚了,我没有足够的注意。您需要处理返回nil的loadScores,当然
scores
应该是
var
而不是
let
,否则您将无法添加到它。有了这些更改,
分数
不再是可选的,因此您不需要将其展开。

最简单的解决方案是将更改后的值从
UITextField
实例保存回
分数视图中的
分数
实例(为什么
score
是可选的,因为您总是通过一个非可选的
score
实例??)并松开该段。 然后将数组保存在
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
中没有存档!

ScoreView
中,您正在存档单个对象,但在另一个控制器中,您正在取消存档一个潜在数组。请添加更多信息,说明您将要完成的任务。@vadian谢谢您的回答,但我该如何做?我不知道您的目标是什么。@vadian这就像一个“Score History”视图,在该视图中,用户将能够看到其所有问答游戏结果……我正在努力研究如何从“ScoreView”场景获取数据,以将其存储到“ScoreHistory”中。"正如@vadian所说,你只写了一个分数历史记录。如果你想保存所有过去的分数,你必须每次都读写所有的分数。这意味着在你的序列中,如果你看起来要在历史记录中添加一个分数,你必须将其全部取消归档,添加一个,然后再次归档。我得到以下e“scores.append(score)”中的错误//类型为“[ScoreHistory]”的值没有成员“append”My bad-您的loadScores函数返回一个可选值,因此需要使用“!”将其展开.但是你考虑过@vadian的解决方案吗?只在一个位置存档/不存档要好得多。ScoreTableViewController中还没有显示分数…我缺少什么?我非常感谢你的帮助。只显示了示例分数。当没有保存分数时,loadScores将返回nil,这不是你想要的。如果是nil,则返回cre我吃了一个新的空数组。我更新了答案。谢谢@vadian,我稍后会尝试并让你知道:)我不太明白,我使用UILabel获取分数,但不使用用户输入获取数据。。抱歉,我对Swifti很陌生如果你只使用标签,为什么要将所有值来回存储到变量中?这些值是从ScoreView自动生成的,例如,在用户完成考试后,ScoreView会显示他正确获得的答案数。。我需要将这些数据存储在ScoreHistory中。对不起,您的代码很混乱。我在代码中看到,您将
viewDidLoad
中的标签设置为变量值(为空字符串),然后如果
score
不是
nil
(但始终不是
nil
),则标签将设置为通过的分数的值,并且再次出现在
视图中。然后在
prepareforsgue
中,将标签中的值存储回新的局部变量,并创建一个新的
ScoreHistory
实例。将通过的
分数保持为模型并只更新标签可能更容易。然后在“展开”任务中,将该实例传递回源代码视图控制器并保存阵列。
  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 ?? ""
    }
  }