Swift:如何使用计时器每秒(或更短)更新标签?

Posted

技术标签:

【中文标题】Swift:如何使用计时器每秒(或更短)更新标签?【英文标题】:Swift: How to update a label every second (or shorter) with a timer? 【发布时间】:2017-08-10 17:56:08 【问题描述】:

我在 Xcode 中为游戏 (SpriteKit) 创建了一个简单的计时器:

变量 timer、seconds 和 score 在类中初始化。

func scoreTimer() 
     timer = Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(countUp), userInfo: nil, repeats: true)
 

@objc func countUp() 
     seconds += 0.05
     seconds = (seconds * 100).rounded() / 100
     score = score + 0.015
     updateScore()
 

 func updateScore() 
     scoreLabel.text = "\(score)"
 

当 GameScene 被加载时,定时器在 didMove() 中启动。

我尝试每 0.5 秒调用一次 countUp()。在这个函数中,变量 seconds 增加并四舍五入加上 score 变量增加。从那里我想调用 updateScore 函数来更新 scoreLabel.text - 但这不起作用。调用了函数,但是 scoreLabel 没有更新,虽然当我尝试打印 score 变量时,它显示它在不断增加。

我怎样才能做到这一点,这里有什么问题?

【问题讨论】:

在 updateScore 方法中打印分数? 只是注意到 0.05 != 0.5。 :) 【参考方案1】:

除非您使用 SpriteKit 之外的技术,否则应避免使用 SpriteKit 使用计时器。原因是因为 SpriteKit 有自己的时间管理,一个定时器可能不会与之重合。例如,在您的情况下,计时器更新分数。如果你暂停游戏怎么办?除非您记得停止计时器,否则您的计时器仍会增加分数。

相反,依靠行动来做​​你需要的事情 下面是你如何做你正在寻找的事情。

let wait = SKAction.wait(forDuration: 0.5)
let update = SKAction.run(

        seconds += 0.05
        seconds = (seconds * 100).rounded() / 100
        score = score + 0.015
        updateScore()

)
let seq = SKAction.sequence([wait,update])
let repeat = SKAction.repeatForever(seq)
run(repeat)

@RonMyschuk 提到了我错过的一个想法,所以我也会将它添加到我的答案中。

如果您需要创建可以单独暂停或在组中暂停的计时器,则添加额外的 SKNode 并将计时器操作附加到节点而不是场景。然后当你需要暂停计时器时,你可以暂停节点。这将允许您的场景更新在您的计时器暂停时继续。

【讨论】:

我可以有多个并行运行吗?例如,一个 SKAction 每 0.5 秒更新一次分数,一个 Action 每 30 秒播放一次随机函数,那么仅使用一个 SKAction 会以不同的方式完成吗? 是的,只要你不为同一个键分配 2 个动作,它们将并行运行,除非你特别把它放在一个序列中 谢谢。我是否将其放入“覆盖函数更新(_ currentTime:TimeInterval)” - 如果是这样,使用什么 TimeInterval? 不,你只在需要的时候调用它一次,把它放在更新中会在每个循环中添加一个新的计时器 谢谢,一切都很好。我想当 GameScene 发生变化(即 GameOverScene 或回到 MenuScene)时,由于更新功能,计时器会自动停止,我不必停止或重置它?【参考方案2】:

我赞同在 Spritekit 中应该避免使用 Timers 的声明。

您还可以使用更新循环更新您的标签。

通过更新循环控制时间允许您在游戏停止时暂停“计时器”,而无需从场景中移除动作。

您还可以使用此方法运行多个计时器

private var updateTime: Double = 0
private var updateTime2: Double = 0

override func update(_ currentTime: CFTimeInterval) 

   //this will be some sort of variable dictating the state of your game
   guard gameState == .playing else  
       updateTime = 0
       updateTime2 = 0
       return 
   

   if updateTime == 0 
       updateTime = currentTime
   

   if updateTime2 == 0 
       updateTime2 = currentTime
   

   if currentTime - updateTime > 0.5 
        //do something every half second
        updateTime = currentTime

        score = score + 0.015
        updateScore()
    

   if currentTime - updateTime2 > 30 
        //do something every 30 seconds
        updateTime2 = currentTime

        someFunc() 
    

【讨论】:

我有点困惑你所说的“如果游戏停止,则暂停“计时器”,而不必从场景中删除动作。”如果场景暂停,那么您的动作也会暂停。 并非所有游戏中的停止都必须通过暂停场景来处理。这种设置方式将决定计时器将针对哪些游戏状态进行计时,以及计时器不会计时的任何其他游戏状态。我个人喜欢使用这种方法,即使我暂停游戏也不暂停场景,因为暂停场景不允许我像暂停菜单中的幻灯片那样执行 SKActions。 啊,好吧,这就是为什么我总是推荐使用 worldNode,它允许您将游戏的行为与场景中可能存在的其他元素分开 我也是这样做的(我使用gameLayer作为我的主节点),我没有在我的回答中涉及到,因为我真的不想让OP太复杂 我明白了,只是想明白你说的游戏停止是什么意思,你的意思是如果游戏处于暂停状态,我还以为你指的是切换应用程序

以上是关于Swift:如何使用计时器每秒(或更短)更新标签?的主要内容,如果未能解决你的问题,请参考以下文章

iOS 8 后台应用程序位置

MySQL PDO 在 3 秒或更短的时间内创建和填充 1000 个小表?

更改标签文本时的延迟响应(Swift)

如何使用来自 Activity 的数据更新小部件?

位置更新的时间间隔更短

使用计时器在模型和标签之间进行数据绑定