我正在使用Google Firestore作为我正在制作的iOS应用程序的后端服务器。我正在尝试使用倒数计时器来表示游戏何时开始,但是,使用网络时间来反对设备时间以确保游戏同时为每个人开始是至关重要的。
基本的想法是从Firebase中检索当前时间戳,从我希望游戏开始时的另一个时间戳值中减去它,然后每秒刷新按钮文本(不是标签,因为我需要能够按下它以进一步显示信息)与时差。
当应用程序启动时,计时器工作得非常好,没有/非常小的延迟,但是,如果我改变我希望游戏开始时的值,按钮文本由于某种原因会出现故障。即使在调试区域,我也可以看到它的滞后。
我做了所有的数学和转换。当前时间戳是timeIntervalSince1970值,我将其转换为格式为“HH:mm:ss”的字符串。我从firebase服务器值检索此值。我希望游戏开始时的另一个时间戳是我存储在Firestore中的集合中的字符串值。
我将这两个字符串值发送到一个函数,找到我的差异,然后将按钮文本设置为值。这很简单,但我无法理解为什么当我更改第二个时间戳值时它会开始滞后。如果我第三次更改它,应用程序仍然运行,但计时器基本上每8秒左右冻结一次。
override func viewDidAppear(_ animated:Bool){
DispatchQueue.main.async {
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
Firestore.firestore().collection("countdown").document("thursday")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let theTime = document.data() else {
print("Document data was empty.")
return
}
print("Current data: \(theTime)")
let stringData = "\(theTime)"
let finalTime = stringData.substring(from: 9, to: 16)
//也许不是获得正确格式的最常规方法,但它对我有用。当然我对这些建议持开放态度。
var currentTimeStamp: Double?
let ref = Database.database().reference().child("serverTimestamp")
ref.setValue(ServerValue.timestamp())
ref.observe(.value, with: { snap in
if let t = snap.value as? Double {
currentTimeStamp = t/1000
let unixTimestamp = currentTimeStamp
let date = Date(timeIntervalSince1970: unixTimestamp!)
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "EDT") //Set timezone that you want
dateFormatter.locale = NSLocale.current
dateFormatter.dateFormat = "HH:mm:ss" //Specify your format that you
let strDate = dateFormatter.string(from: date)
let dateDiff = self.findDateDiff(time1Str: strDate, time2Str: finalTime)
print("This is the countdown time: \(dateDiff)")
}
})
}
}
}
//这只是函数内部的一个峰值,可以完成所有数学运算,并在按钮文本中输出倒计时时间。我有很多if语句用于所有场景的时间来计算“HH:mm:ss”格式。
如果hours> = 10 && minutes> = 10 && seconds <10 {
self.time.setTitle(("\(Int(hours)):\(Int(minutes)):0\(Int(seconds))"), for: .normal)
我希望按钮文本能够以正确的倒计时时间刷新。我希望当我更改我的Firestore时间戳值时,按钮文本会自动刷新并且不会延迟。
实际结果是它滞后了。
所以我不确定我是否有这个正确,但我的理解是你设置一个Timer
对象每秒发射一次.observe
呼叫?如果是这样,您的Firebase调用没有及时检索数据(.observe
是异步的,所以这就是延迟的来源)。
我建议做的是首先设置你倒计时的时间戳并将该值发布到Firebase。
当每个设备需要倒计时按钮时,它们会检索时间戳,然后将Timer
对象设置为以1秒为间隔触发,以从NSDate()。timeIntervalFrom1970之类的内容中检索当前时间,找到两者之间的差异,然后设置按钮文本(倒计时结束后停用Timer
)。也许这样的事情?:
override func viewDidAppear(_ animated: Bool) {
// get time to count down from
Firestore.firestore().collection("countdown").document("thursday").getDocument { (document, error) in
guard let document = document, document.exists else {
// document doesn't exist
return
}
guard let theTime = document.data() else {
print("Document data was empty.")
return
}
print("Current data: \(theTime)")
let stringData = "\(theTime)"
let finalTime = stringData.substring(from: 9, to: 16)
self.setTimer(for: finalTime)
}
}
// set Timer
func setTimer(for finalTime: String) {
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
let timeNow = Date()
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "EDT")
dateFormatter.locale = NSLocale.current
dateFormatter.dateFormat = "HH:mm:ss"
let strDate = dateFormatter.string(from: timeNow)
// stop timer once finalTime reached. Idk what finalDate is but here's if it's formatted the same as strDate is.
guard finalTime != strDate else {
timer.invalidate()
print("countdown over")
return
}
let dateDiff = self.findDateDiff(time1Str: strDate, time2Str: finalTime)
print("This is the countdown time: \(dateDiff)")
}
timer.fire()
}
另外,我建议将时间戳值比较为双打,然后根据需要格式化差异,而不是格式化每个变量,然后将它们作为字符串进行比较