How can I use Timer (formerly NSTimer) in Swift?
This will work:
override func viewDidLoad() {
super.viewDidLoad()
// Swift block syntax (iOS 10+)
let timer = Timer(timeInterval: 0.4, repeats: true) { _ in print("Done!") }
// Swift >=3 selector syntax
let timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
// Swift 2.2 selector syntax
let timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: #selector(MyClass.update), userInfo: nil, repeats: true)
// Swift <2.2 selector syntax
let timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
}
// must be internal or public.
@objc func update() {
// Something cool
}
For Swift 4, the method of which you want to get the selector must be exposed to Objective-C, thus @objc
attribute must be added to the method declaration.
How can I set up timer in Swift?
There are a few issues here:
Every time
onTimerFires
is called, it is decrementingtimeHours
(which does not appear to be set or used anywhere) and then callstimeFunc
, which just grabs values from the picker (which are not changing anywhere) to build the string.This pattern of decrementing a counter in a timer makes a big assumption, namely that the timer is invoked exactly at the right time and that it will not ever miss a timer call. That is not a prudent assumption, unfortunately.
I would advise against the
Selector
based timer, as that introduces a strong reference cycle with thetarget
. The block-based rendition withweak
references is easier to avoid these sorts of cycles.
I would suggest a different pattern, namely that you save the time to which you are counting down:
var countDownDate: Date!
func updateCountDownDate() {
let components = DateComponents(hour: pickerView.selectedRow(inComponent: 0),
minute: pickerView.selectedRow(inComponent: 1),
second: pickerView.selectedRow(inComponent: 2))
countDownDate = Calendar.current.date(byAdding: components, to: Date())
}
Then your timer handler can calculate the amount of elapsed time to the target count down date/time:
func handleTimer(_ timer: Timer) {
let remaining = countDownDate.timeIntervalSince(Date())
guard remaining >= 0 else {
timer.invalidate()
return
}
label.text = timeString(from: remaining)
}
When you start the timer, you calculate the countDownDate
and schedule the timer:
weak var timer: Timer?
func startTimer() {
timer?.invalidate() // if one was already running, cancel it
updateCountDownDate()
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
guard let self = self else {
timer.invalidate()
return
}
self.handleTimer(timer)
}
timer?.fire()
}
And, for what is worth, when preparing the string, you certainly can determine the date components between to dates (i.e., now and your target date/time), but you can also use a DateComponentsFormatter
do this for you:
let formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
formatter.allowedUnits = [.hour, .minute, .second]
formatter.zeroFormattingBehavior = .pad
return formatter
}()
func timeString(from interval: TimeInterval) -> String? {
return formatter.string(from: interval)
}
Pass a Swift function to NSTimer
If you are targeting pre-iOS 10, you can't pass a function to NSTimer
because no API was introduced at that time to support closure callbacks.
iOS 10 and later Approach
// swift 2.x users should still use NSTimer instead
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { timer in
// ...
}
Generic Approach
You can add this class, and reuse it anytime:
final class TimerInvocation: NSObject {
var callback: () -> ()
init(callback: @escaping () -> ()) {
self.callback = callback
}
func invoke() {
callback()
}
}
extension Timer {
static func scheduleTimer(timeInterval: TimeInterval, repeats: Bool, invocation: TimerInvocation) {
Timer.scheduledTimer(
timeInterval: timeInterval,
target: invocation,
selector: #selector(TimerInvocation.invoke(timer:)),
userInfo: nil,
repeats: repeats)
}
}
With this class, you can simply do this now:
let invocation = TimerInvocation {
/* invocation code here */
}
NSTimer.scheduledTimerWithTimeInterval(1, target: invocation, selector:#selector(TimerInvocation.invoke), userInfo: nil, repeats: false)
You don't have to worry about retaining the invocation
variable since it is retained by NSTimer
How to switch execution based on NSTimer every 30 seconds?
Keep track of a timer instance with:
var timer: Timer!
Then update both functions to call the other after 30 seconds:
func first30Seconds() {
timer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(second30Seconds), userInfo:nil, repeats: false)
// Other code...
}
func second30Seconds() {
timer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(first30Seconds), userInfo:nil, repeats: false)
// Other code...
}
How do I use NSTimer to countdown from 10 seconds then stop the timer?
Define the timer as a var in your class:
var timer = NSTimer()
Create the timer in viewDidLoad:
timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
Update than will be called every 0.4 second (more or less):
func update() {
if(count > 0)
{
countLabel1.text = String(count--)
} else {
timer.invalidate()
}}
Edit: [If you want to get update called every second put 1 instead of 0.4 of course.]
NSTimer - how to delay in Swift
Swift 3
With GCD:
let delayInSeconds = 4.0
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayInSeconds) {
// here code perfomed with delay
}
or with a timer:
func myPerformeCode() {
// here code to perform
}
let myTimer : Timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(self.myPerformeCode), userInfo: nil, repeats: false)
Swift 2
With GCD:
let seconds = 4.0
let delay = seconds * Double(NSEC_PER_SEC) // nanoseconds per seconds
let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
// here code perfomed with delay
})
or with a timer:
func myPerformeCode(timer : NSTimer) {
// here code to perform
}
let myTimer : NSTimer = NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: Selector("myPerformeCode:"), userInfo: nil, repeats: false)
Swift Run An NSTimer Automatically
Swift 2.x :
func DisplayTimer() {
Timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target:self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
}
func updateTimer() {
if Counter != 0 { TimerLabel.text = "\(Counter -= 1)"
} else {
Timer.invalidate()
// call a game over method here...
}
}
override func viewDidLoad() {
super.viewDidLoad()
...
// start the timer when this controller shows up
DisplayTimer()
TimerLabel.text = "\(Counter)"
...
}
Related Topics
Post Request Using Application/X-Www-Form-Urlencoded
How to Pass Prepareforsegue: an Object
How to Determine the Current Iphone/Device Model
How to Store Custom Objects in Nsuserdefaults
How to Size a Uitextview to Its Content
How to Get the Indexpath.Row When an Element Is Activated
What Happens When Testflight App Expire
How to Change Button Title Alignment in Swift
Iphone 6 and 6 Plus Media Queries
Returning Data from Async Call in Swift Function
Transport Security Has Blocked a Cleartext Http
How to Check For an Active Internet Connection on iOS or Macos
Loading/Downloading Image from Url on Swift
How to Deal With the Nsdateformatter Locale "Feechur"
Uidevice Uniqueidentifier Deprecated - What to Do Now