在 Swift 中使用 NSTimer
Posted
技术标签:
【中文标题】在 Swift 中使用 NSTimer【英文标题】:Using an NSTimer in Swift 【发布时间】:2014-08-13 17:02:58 【问题描述】:在这种情况下,永远不会调用 timerFunc()。我错过了什么?
class AppDelegate: NSObject, NSApplicationDelegate
var myTimer: NSTimer? = nil
func timerFunc()
println("timerFunc()")
func applicationDidFinishLaunching(aNotification: NSNotification?)
myTimer = NSTimer(timeInterval: 5.0, target: self, selector:"timerFunc", userInfo: nil, repeats: true)
【问题讨论】:
如果你使用定时器的init
你必须You must add the new timer to a run loop, using addTimer:forMode:
。这是文档描述中的第二句话。否则使用scheduledTimerWithTimeInterval
,这可能就是您要查找的内容。
为什么考虑到几个人提供了不同的答案而投反对票?
不能肯定地告诉你,但可能是因为答案并不难找到,正如我之前指出的那样。它在文档中是正确的。如果您让option
点击您的方法,您将在 5 秒内找到解决方案,甚至无需离开 Xcode。
【参考方案1】:
您可以创建一个定时定时器,它会自动将自身添加到运行循环并开始触发:
斯威夫特 2
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "timerDidFire:", userInfo: userInfo, repeats: true)
斯威夫特 3、4、5
Timer.scheduledTimer(withTimeInterval: 0.5, target: self, selector: #selector(timerDidFire(_:)), userInfo: userInfo, repeats: true)
或者,您可以保留当前代码,并在准备好时将计时器添加到运行循环:
斯威夫特 2
let myTimer = NSTimer(timeInterval: 0.5, target: self, selector: "timerDidFire:", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(myTimer, forMode: NSRunLoopCommonModes)
斯威夫特 3、4、5
let myTimer = Timer(timeInterval: 0.5, target: self, selector: #selector(timerDidFire(_:)), userInfo: nil, repeats: true)
RunLoop.current.add(myTimer, forMode: RunLoop.Mode.common)
【讨论】:
如何暂停和恢复这个计时器? myTimer.invalidate() 取消它。除此之外,NSTimer 不支持任何暂停/恢复功能。您可以想象一个简单的子类,它通过 (1) 记录开始时间、(2) 记录暂停时间和 (3) 在恢复时添加这种支持,运行时间比我们原来的 timeInterval 更短的 t2-t1。 Sift 3.0 的运行循环语法:RunLoop.current.add(myTimer, forMode: RunLoopMode.commonModes) Swift3RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
【参考方案2】:
我使用与卢克类似的方法。 仅对“私人方法”纯粹主义者的警告:
不要在 Swift 中将回调设为私有。
如果你写:
private func timerCallBack(timer: NSTimer)
..
你会得到:
timerCallBack:]: 无法识别的选择器发送到实例...由于未捕获的异常“NSInvalidArgumentException”而终止应用程序
【讨论】:
非常感谢! 只需将@objc
添加到私有方法中,它也可以工作:@objc private func timerCallBack(timer: NSTimer)
@Lensflare 的建议答案是正确的,你只需要 @objc
方法装饰器。
是的,我知道“@objc”,但我们说的是“swift”纯粹主义者......所以避免来自 objc 的所有旧交易。 (我也喜欢它..)【参考方案3】:
除非您使用NSTimer.scheduledTimerWithTimeInterval
,否则不会自动安排NSTimer:
myTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "timerFunc", userInfo: nil, repeats: true)
【讨论】:
【参考方案4】:正如 Drewag 和 Ryan 指出的那样,您需要创建一个预定的计时器(或自己安排)。最简单的方法是使用以下方法创建它:
myTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "timerFunc:", userInfo: nil, repeats: true)
您还需要更改 timerFunc(以及相关的选择器)的定义以获取参数并以“:”结尾
func timerFunc(timer:NSTimer!)
...
【讨论】:
你不需要定义它来接受一个参数。无论哪种方式都可以。 @drewag 好的,我只记得关于 SO 的一些讨论,如果没有 ':',它就无法工作,所以我在操场上试了一下。如果您(正确地)将回调定义为带参数,则需要“:”。 “正确”,因为NSTimer
文档说回调必须采用单个参数。 “选择器应具有以下签名:timerFireMethod:(包括一个冒号,表示该方法需要一个参数)。”似乎您可以在没有 : 或参数的情况下逃脱,但我仍然会说根据文档我的答案是正确的 :)
这些参数在 IBActions 和我曾经定义过选择器的任何其他地方也是可选的(至少只有一个参数)。这只是运行时的本质。他忽略了论点的事实与为什么不调用它无关。最坏的情况是它会抛出一个关于找不到选择器的异常,而不是静默失败。【参考方案5】:
对于 Swift 3
var timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(ViewController.updateTimer), userInfo: nil, repeats: true);
RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
【讨论】:
使用scheduledTimer()
方法会自动将你的Timer添加到runloop——你不需要手动添加。【参考方案6】:
运行循环的 Swift 3.0 语法:
RunLoop.current.add(myTimer, forMode: .commonModes)
【讨论】:
【参考方案7】:这是一段代码,演示了如何在不带参数的情况下使用 AND 调用函数(延迟)。
在 xCode (singleViewApplication) 的新项目中使用它,并将代码放入标准 viewController:
class ViewController: UIViewController
override func viewDidLoad()
super.viewDidLoad()
NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: Selector("delayedFunctionWithoutParameter:"), userInfo: nil, repeats: false)
let myParameter = "ParameterStringOrAnyOtherObject"
NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: Selector("delayedFunctionWithParameter:"), userInfo: myParameter, repeats: false)
// SIMPLE TIMER - Delayed Function Call
func delayedFunctionWithoutParameter(timer : NSTimer)
print("This is a simple function beeing called without a parameter passed")
timer.invalidate()
// ADVANCED TIMER - Delayed Function Call with a Parameter
func delayedFunctionWithParameter(timer : NSTimer)
// check, wether a valid Object did come over
if let myUserInfo: AnyObject = timer.userInfo
// alternatively, assuming it is a String for sure coming over
// if let myUserInfo: String = timer.userInfo as? String
// assuming it is a string comming over
print("This is an advanced function beeing called with a parameter (in this case: \(myUserInfo)) passed")
timer.invalidate()
注意,在任何情况下,您都应该使用参数 (timer : NSTimer) 实现延迟函数,以便能够使计时器无效(终止、结束)。通过 passend “计时器”,您还可以访问 userInfo(您可以在其中放置任何对象,不仅是字符串对象,还可以放置数组和字典等集合类型)。
原始 Apples 文档说“” -> 计时器将自身作为参数传递,因此该方法将采用以下模式: - (void)timerFireMethod:(NSTimer *)timer 完整阅读 -> here
【讨论】:
【参考方案8】:由于这个帖子让我自己尝试将计时器放在 RunLoop 上(这解决了我的问题),我也发布了我的具体案例 - 谁知道它可能对某人有所帮助。
我的计时器是在应用程序启动和所有对象的初始化期间创建的。我的问题是,虽然它确实安排了计时器,但它仍然没有触发。我的猜测是,之所以出现这种情况,是因为scheduledTimerWithTimeInterval
在应用程序启动期间将计时器置于不同的 RunLoop 上。如果我只是初始化计时器然后使用NSRunLoop.mainRunLoop().addTimer(myTimer, forMode:NSDefaultRunLoopMode)
,它工作正常。
【讨论】:
【参考方案9】:使用 swift3,你可以运行它,
var timer: Timer?
func startTimer()
if timer == nil
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.loop), userInfo: nil, repeats: true)
func stopTimer()
if timer != nil
timer?.invalidate()
timer = nil
func loop()
//do something
【讨论】:
【参考方案10】:要使用 OP 建议的方法,您需要将其添加到运行循环中:
myTimer = NSTimer(timeInterval: 5.0, target: self, selector:"timerFunc", userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(myTimer, forMode:NSDefaultRunLoopMode)
文档还说目标应该接受一个参数,但没有它它也可以工作。
func timerFireMethod(timer: NSTimer)
【讨论】:
以上是关于在 Swift 中使用 NSTimer的主要内容,如果未能解决你的问题,请参考以下文章
swift 优雅地处理Swift中可本地化的字符串。注意:此代码在Swift 2中,需要在现代Swift中使用更新。
在 Swift 项目中使用 Objective C 类中的 Swift 类
在 Swift 项目中使用 Objective C 类中的 Swift 类