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
Getting Uitableview Error "Unable to Dequeue a Cell with Identifier Cell"
Swift Bridging Header File Won't Work with Use_Frameworks
Passing Data Between View Controllers in Swift (From Tableview to Detailviewcontroller)
Cannot Invoke 'Decode' with an Argument List of Type '(T, From: Data)'
Retrieving Uiimage from Uiimageview in Swift
Schedule Local Notification Every N Days (Timezone Safe)
How to Use Alamofires Servertrustpolicy.Disableevaluation in Swift 3
How to Update a Sent Message in Quickblox iOS
Non-Main Bundle File as Alert Sound
Ios: How to Create Expandable Tableview in Swift Without Using Third Party Libraries or Pods
Get Total Include Months Between 2 Date in Swift
How to Turn Off Core Data Write-Ahead Logging in Swift Using Options Dictionary
Firebase: Provided Bucket Does Not Match the Storage Bucket of the Current Instance in Swift
My Reachability Notifier Is Only Able to Be Called Once