如何在 swift 中记录日期、时间和分数
How to record date, time, and score in swift
我正在创建一个简单的测验应用程序。我打算展示某种 "history",用户可以在其中看到以下内容:
- 播放日期和时间
- 该特定会话的分数
我该怎么做?
截至播放日期和时间,我在 SO 上看到了这个帖子:How to get the current time as datetime
但是,如何 "RECORD" 用户玩游戏的日期和时间?
关于Score数据,我使用的是:
NSUserDefaults.standardUserDefaults().setInteger(currentScore, forKey: "score")
但是,我只能得到当前分数。如何记录用户在不同日期和时间获得的每个会话的分数?
请注意,我在获取用户的当前分数方面没有问题。我需要帮助来存储或记录用户在多个会话中的分数。
例如,我想显示这样的东西:
Date: 2/7/16
Time: 7:00 AM
Score: 70/100
要存储每个会话的分数,您需要某种与每个用户关联的数据结构。您可以使用将日期与分数相关联的键值对字典。这样,您将为用户存储的每个日期都有一个分数。
您需要使用数据库来存储此类具有未定义记录数的数据。
查看 Apple 的核心数据编程指南 here
然后您可以创建一个名为 history
的实体,在其中存储用户的游戏历史记录,方法是在用户每次完成游戏时向该实体中插入一个新对象。
当您需要显示结果时,您可以在 NSManagedObjectContext
上创建一个 NSFetchRequest
以获得所有结果,并根据需要 display/filter 它们。
希望对您有所帮助!
NSUserDefaults
可能不适合您要执行的操作。我建议使用 NSCoding
进行简单的数据存储。对于这么简单的事情,核心数据可能太复杂了。但是,如果您计划保存具有关系的大型数据模型,那么 Core Data 就是您的不二之选。
NS编码
NSCoding
有两部分:
- 编解码
- 归档和取消归档
NSHipster 完美解释:
NSCoding is a simple protocol, with two methods: -initWithCoder: and encodeWithCoder:. Classes that conform to NSCoding can be serialized and deserialized into data that can be either be archived to disk or distributed across a network.
归档由 NSKeyedArchiver
和 NSKeyedUnarchiver
执行。
会话
即使没有NSCoding
,也建议用对象来表示数据。在这种情况下,我们可以使用非常有创意的名称Session
来表示历史记录中的一个会话。
class Session: NSObject, NSCoding {
let date: NSDate // stores both date and time
let score: Int
init(date: NSDate, score: Int) { // initialize a NEW session
self.date = date
self.score = score
super.init()
}
required init?(coder aDecoder: NSCoder) { // decodes an EXISTING session
if let decodedDate = aDecoder.decodeObjectForKey("date") as? NSDate {
self.date = decodedDate
} else {
self.date = NSDate() // placeholder // this case shouldn't happen, but clearing compiler errors
}
self.score = aDecoder.decodeIntegerForKey("score")
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(date, forKey: "date")
aCoder.encodeInteger(score, forKey: "score")
}
}
以上英文代码,从上到下依次为:
- 定义class,符合
NSCoding
- 会话的属性:日期(+时间)和分数
- 新会话的初始化程序 - 只需获取日期和分数并为其创建会话
- 现有会话所需的初始化程序 - 解码保存的日期和分数
decodeObjectForKey:
只是按照它说的去做(使用密钥解码一个对象),它 returns AnyObject?
decodeIntegerForKey:
然而,returns Int
。如果 none 存在于文件中,则它是 returns 0,这就是它不是可选的原因。大多数解码方法都是这种情况,除了 decodeObjectForKey:
- 编码现有会话所需的方法 - 编码日期和分数
- 编码方法和解码方法一样简单。
这会处理 Session
class,并为 NSCoding
准备好属性。当然,您可以随时添加更多属性和方法。
会话历史记录
虽然会话本身很好,但需要一个对象来管理会话数组,并且它还需要符合 NSCoding
。您还可以将此代码添加到现有的 class.
class SessionHistory: NSObject, NSCoding {
var sessions = [Session]()
required init?(coder aDecoder: NSCoder) {
if let decodedSessions = aDecoder.decodeObjectForKey("sessions") as? [Session] {
self.sessions = decodedSessions
} else {
self.sessions = [] // another compiler error clearer
}
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(sessions, forKey: "sessions")
}
override init() { // Used for convenience
super.init()
}
}
英文翻译:
- 定义管理器,符合
NSCoding
- 为会话数组添加 属性
- 接下来的两个
NSCoding
方法几乎与 Session
做同样的事情。除了这次,它是一个数组。
- 新管理器的初始化程序,将在下面使用。
NSCoding
查看此管理器 class 并发现它需要对会话数组进行编码,因此 NSCoding
查看 Session
class 查看为这些会话编码的内容。
NSKeyedArchiver/NSKeyedUnarchiver 和单例
现在所有 NSCoding
都已设置,最后一步是合并 NSKeyedArchiver
和 NSKeyedUnarchiver
以实际保存和加载数据。
两个重要的方法是NSKeyedArchiver.archiveRootObject(_, toFile:)
和NSKeyedUnarchiver.unarchiveRootObjectWithFile:
请注意,这两种方法都需要一个文件。它会自动为您创建文件,但您需要设置一个位置。将此添加到 SessionHistory
:
static var dataPath: String {
let URLs = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let URL = URLs[0]
return URL.URLByAppendingPathComponent("savehistory").path! // Put anything you want for that string
}
这只是找到文件的位置。当然,您可以找到其他地方来放置文件。
准备好数据路径后,您可以使用我前面提到的两种方法。我喜欢为管理器 class 使用修改后的单例版本,以确保我使用的是相同的对象数组。在SessionHistory
class:
private static var history: SessionHistory!
static func appHistory() -> SessionHistory {
if history == nil {
if let data = NSKeyedUnarchiver.unarchiveObjectWithFile(dataPath) as? SessionHistory {
history = data
} else {
history = SessionHistory()
}
}
return history
}
这会创建一个私有静态 属性 来存储应用程序的单次会话历史记录。静态方法检查会话历史记录是否为 nil
。如果是这样,它将 returns 文件中的当前历史记录并将文件加载到历史记录 属性 中。否则,它会创建一个新的空会话历史记录。在那之后,或者如果历史 属性 已经存储了一些东西,它 returns 历史 属性.
用法
NSCoding
和 NSKeyedArchiver
的所有设置已完成。但是你如何使用这个代码?
每次您想访问会话历史记录时,请调用
SessionHistory.appHistory()
无论您想在哪里保存会话历史记录,请调用
NSKeyedArchiver.archiveRootObject(SessionHistory.appHistory(), toFile: SessionHistory.dataPath)
示例用法如下:
let session = Session(date: someRandomDate, score: someRandomScore)
SessionHistory.appHistory().sessions.append(session)
NSKeyedArchiver.archiveRootObject(SessionHistory.appHistory(), toFile: SessionHistory.dataPath)
当通过 SessionHistory.appHistory()
访问时,会话历史记录将自动从文件中加载。
您实际上并不需要 "link" classes 本身,您只需将会话附加到会话历史记录的 sessions
数组中即可。
进一步阅读
- NSHipster 是对
NSCoding
的简单介绍。
- Apple's NSCoding guide,虽然年龄较大并且在 Objective-C,但更深入
NSCoding
。
我正在创建一个简单的测验应用程序。我打算展示某种 "history",用户可以在其中看到以下内容:
- 播放日期和时间
- 该特定会话的分数
我该怎么做?
截至播放日期和时间,我在 SO 上看到了这个帖子:How to get the current time as datetime 但是,如何 "RECORD" 用户玩游戏的日期和时间?
关于Score数据,我使用的是:
NSUserDefaults.standardUserDefaults().setInteger(currentScore, forKey: "score")
但是,我只能得到当前分数。如何记录用户在不同日期和时间获得的每个会话的分数?
请注意,我在获取用户的当前分数方面没有问题。我需要帮助来存储或记录用户在多个会话中的分数。
例如,我想显示这样的东西:
Date: 2/7/16
Time: 7:00 AM
Score: 70/100
要存储每个会话的分数,您需要某种与每个用户关联的数据结构。您可以使用将日期与分数相关联的键值对字典。这样,您将为用户存储的每个日期都有一个分数。
您需要使用数据库来存储此类具有未定义记录数的数据。 查看 Apple 的核心数据编程指南 here
然后您可以创建一个名为 history
的实体,在其中存储用户的游戏历史记录,方法是在用户每次完成游戏时向该实体中插入一个新对象。
当您需要显示结果时,您可以在 NSManagedObjectContext
上创建一个 NSFetchRequest
以获得所有结果,并根据需要 display/filter 它们。
希望对您有所帮助!
NSUserDefaults
可能不适合您要执行的操作。我建议使用 NSCoding
进行简单的数据存储。对于这么简单的事情,核心数据可能太复杂了。但是,如果您计划保存具有关系的大型数据模型,那么 Core Data 就是您的不二之选。
NS编码
NSCoding
有两部分:
- 编解码
- 归档和取消归档
NSHipster 完美解释:
NSCoding is a simple protocol, with two methods: -initWithCoder: and encodeWithCoder:. Classes that conform to NSCoding can be serialized and deserialized into data that can be either be archived to disk or distributed across a network.
归档由 NSKeyedArchiver
和 NSKeyedUnarchiver
执行。
会话
即使没有NSCoding
,也建议用对象来表示数据。在这种情况下,我们可以使用非常有创意的名称Session
来表示历史记录中的一个会话。
class Session: NSObject, NSCoding {
let date: NSDate // stores both date and time
let score: Int
init(date: NSDate, score: Int) { // initialize a NEW session
self.date = date
self.score = score
super.init()
}
required init?(coder aDecoder: NSCoder) { // decodes an EXISTING session
if let decodedDate = aDecoder.decodeObjectForKey("date") as? NSDate {
self.date = decodedDate
} else {
self.date = NSDate() // placeholder // this case shouldn't happen, but clearing compiler errors
}
self.score = aDecoder.decodeIntegerForKey("score")
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(date, forKey: "date")
aCoder.encodeInteger(score, forKey: "score")
}
}
以上英文代码,从上到下依次为:
- 定义class,符合
NSCoding
- 会话的属性:日期(+时间)和分数
- 新会话的初始化程序 - 只需获取日期和分数并为其创建会话
- 现有会话所需的初始化程序 - 解码保存的日期和分数
decodeObjectForKey:
只是按照它说的去做(使用密钥解码一个对象),它 returnsAnyObject?
decodeIntegerForKey:
然而,returnsInt
。如果 none 存在于文件中,则它是 returns 0,这就是它不是可选的原因。大多数解码方法都是这种情况,除了decodeObjectForKey:
- 编码现有会话所需的方法 - 编码日期和分数
- 编码方法和解码方法一样简单。
这会处理 Session
class,并为 NSCoding
准备好属性。当然,您可以随时添加更多属性和方法。
会话历史记录
虽然会话本身很好,但需要一个对象来管理会话数组,并且它还需要符合 NSCoding
。您还可以将此代码添加到现有的 class.
class SessionHistory: NSObject, NSCoding {
var sessions = [Session]()
required init?(coder aDecoder: NSCoder) {
if let decodedSessions = aDecoder.decodeObjectForKey("sessions") as? [Session] {
self.sessions = decodedSessions
} else {
self.sessions = [] // another compiler error clearer
}
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(sessions, forKey: "sessions")
}
override init() { // Used for convenience
super.init()
}
}
英文翻译:
- 定义管理器,符合
NSCoding
- 为会话数组添加 属性
- 接下来的两个
NSCoding
方法几乎与Session
做同样的事情。除了这次,它是一个数组。 - 新管理器的初始化程序,将在下面使用。
NSCoding
查看此管理器 class 并发现它需要对会话数组进行编码,因此 NSCoding
查看 Session
class 查看为这些会话编码的内容。
NSKeyedArchiver/NSKeyedUnarchiver 和单例
现在所有 NSCoding
都已设置,最后一步是合并 NSKeyedArchiver
和 NSKeyedUnarchiver
以实际保存和加载数据。
两个重要的方法是NSKeyedArchiver.archiveRootObject(_, toFile:)
和NSKeyedUnarchiver.unarchiveRootObjectWithFile:
请注意,这两种方法都需要一个文件。它会自动为您创建文件,但您需要设置一个位置。将此添加到 SessionHistory
:
static var dataPath: String {
let URLs = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let URL = URLs[0]
return URL.URLByAppendingPathComponent("savehistory").path! // Put anything you want for that string
}
这只是找到文件的位置。当然,您可以找到其他地方来放置文件。
准备好数据路径后,您可以使用我前面提到的两种方法。我喜欢为管理器 class 使用修改后的单例版本,以确保我使用的是相同的对象数组。在SessionHistory
class:
private static var history: SessionHistory!
static func appHistory() -> SessionHistory {
if history == nil {
if let data = NSKeyedUnarchiver.unarchiveObjectWithFile(dataPath) as? SessionHistory {
history = data
} else {
history = SessionHistory()
}
}
return history
}
这会创建一个私有静态 属性 来存储应用程序的单次会话历史记录。静态方法检查会话历史记录是否为 nil
。如果是这样,它将 returns 文件中的当前历史记录并将文件加载到历史记录 属性 中。否则,它会创建一个新的空会话历史记录。在那之后,或者如果历史 属性 已经存储了一些东西,它 returns 历史 属性.
用法
NSCoding
和 NSKeyedArchiver
的所有设置已完成。但是你如何使用这个代码?
每次您想访问会话历史记录时,请调用
SessionHistory.appHistory()
无论您想在哪里保存会话历史记录,请调用
NSKeyedArchiver.archiveRootObject(SessionHistory.appHistory(), toFile: SessionHistory.dataPath)
示例用法如下:
let session = Session(date: someRandomDate, score: someRandomScore)
SessionHistory.appHistory().sessions.append(session)
NSKeyedArchiver.archiveRootObject(SessionHistory.appHistory(), toFile: SessionHistory.dataPath)
当通过 SessionHistory.appHistory()
访问时,会话历史记录将自动从文件中加载。
您实际上并不需要 "link" classes 本身,您只需将会话附加到会话历史记录的 sessions
数组中即可。
进一步阅读
- NSHipster 是对
NSCoding
的简单介绍。 - Apple's NSCoding guide,虽然年龄较大并且在 Objective-C,但更深入
NSCoding
。