Countdown with Several Decimal Slots, Using Nstimer in Swift

Countdown with several decimal slots, using NSTimer in Swift

Timers are not super-accurate, and the resolution of NSTimer is about 1/50th of a second.

Plus, the refresh rate of the iPhone screen is 60 frames/second, so it's totally pointless to run your timer any faster than that.

Rather than trying to use a timer to decrement something every time it fires, create a timer that fires like 50 times a second, and have it use clock math to update the display based on the remaining time:

var futureTime: NSTimeInterval 

override func viewDidLoad() {
super.viewDidLoad()
labelValue = counter

//FutureTime is a value 10 seconds in the future.
futureTime = NSDate.timeIntervalSinceReferenceDate() + 10.0

var timer = NSTimer.scheduledTimerWithTimeInterval(
0.02,
target: self,
selector: ("update:"),
userInfo: nil,
repeats: true)
}

func update(timer: NSTimer)
{
let timeRemaining = futureTime - NSDate.timeIntervalSinceReferenceDate()
if timeRemaining > 0.0
{
label.text = String(format: "%.07f", timeRemaining)
}
else
{
timer.invalidate()
//Force the label to 0.0000000 at the end
label.text = String(format: "%.07f", 0.0)
}
}

Swift timer in milliseconds

As Martin says in his comment, timers have a resolution of 50-100 ms (0.05 to 0.1 seconds). Trying to run a timer with an interval shorter than that will not give reliable results. Also, timers are not realtime. They depend on the run loop they are attached to, and if the run loop gets busy, the firing of the timer gets delays.

Instead of trying to increment a counter each time your timer fires, record the start time when you initiate the timer, and then do some math to figure out how much time has transpired:

var startTime: NSTimeInterval

//Sart the timer
startTime = NSDate.timeIntervalSinceReferenceDate()
NSTimer.scheduledTimerWithTimeInterval(0.02,
target: self,
selector: Selector("advanceTimer:"),
userInfo: nil,
repeats: true)

//And your timer method...
func advanceTimer(timer: NSTimer)
{
//Total time since timer started, in seconds
self.time = NSDate.timeIntervalSinceReferenceDate() - startTime
//The rest of your code goes here
}

EDIT:

The Swift 3 version of this code looks like this:

(Written as a view controller in a test project)

class ViewController: UIViewController {

weak var timer: Timer?
var startTime: Double = 0
var time: Double = 0

@IBOutlet weak var timeValueLabel: UILabel!

/*
When the view controller first appears, record the time and start a timer
*/
override func viewDidAppear(_ animated: Bool) {
startTime = Date().timeIntervalSinceReferenceDate
timer = Timer.scheduledTimer(timeInterval: 0.05,
target: self,
selector: #selector(advanceTimer(timer:)),
userInfo: nil,
repeats: true)
}

//When the view controller is about to disappear, invalidate the timer
override func viewWillDisappear(_ animated: Bool) {
timer?.invalidate()
}

func advanceTimer(timer: Timer) {

//Total time since timer started, in seconds
time = Date().timeIntervalSinceReferenceDate - startTime

//The rest of your code goes here

//Convert the time to a string with 2 decimal places
let timeString = String(format: "%.2f", time)

//Display the time string to a label in our view controller
timeValueLabel.text = timeString
}
}

Why is this sleep function not working when I try to animate rain?

The UI is updated only when program control returns to the main event
loop. Therefore your modifications to r1.center will become visible
only when the click() method returns.

Also you are blocking the main thread for a long time, which is generally bad because no events are processed and the UI is not updated, so the
app becomes unresponsive.
Blocking the main thread for too long may cause the program to be killed by the OS.

You can use a timer instead with a callback function which updates the position.

Alternatively (as mentioned in a comment), use
Core Animation.

RxSwift Countdown time up to 0.1 seconds

Use .milliseconds(100) instead of .seconds(1)



Related Topics



Leave a reply



Submit