How to Use Timer (Formerly Nstimer) in Swift

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 decrementing timeHours (which does not appear to be set or used anywhere) and then calls timeFunc, 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 the target. The block-based rendition with weak 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



Leave a reply



Submit