简体   繁体   中英

Swift4 Animation using Timer.scheduledTimer

I'm animating a clock hand that takes a CGFloat value from 0 to 1. While I have the animation, I would like it to be a lot smoother. The total animation takes 5 seconds, as part of an input variable. How can I make this a lot smoother?

Ideally, I'd like to get all the values from 0 to 1 in 5 seconds...

The clock hand does a complete 360 but is a little choppy

@IBAction func start(_ sender: Any) {

    timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(launchTimer), userInfo: nil, repeats: true)

    launchTimer()
}

 func launchTimer()  {

    guard seconds < 4.9 else {

        timer.invalidate()
        seconds = 0

        return
    }

    seconds += 0.1

    clockView.currentPressure = CGFloat(seconds / 5)
    clockView.setNeedsDisplay()

}

EDIT

import UIKit

class GaugeView: UIView {

    var currentPressure : CGFloat = 0.0

    override func draw(_ rect: CGRect) {
        StyleKitName.drawGauge(pressure: currentPressure)
    }
}

Make the time interval smaller to make the animation smoother. That way it will seem like it's gliding around instead of jumping between values.

You can also use spritekit:

import SpriteKit

let wait = SKAction.wait(forDuration: 0.01)
let runAnim = SKAction.run {
    launchTimer()
}
let n = SKNode()
n.run(SKAction.repeat(SKAction.sequence([wait, runAnim]), count: 500))

Timer is not appropriate for animations on this scale. 100ms isn't a good step in any case, since it's not a multiple of the frame rate (16.67ms). Generally speaking, you shouldn't try to hand-animate unless you have a specialized problem. See UIView.animate(withDuration:...) , which is generally how you should animate UI elements, allowing the system to take care of the progress for you.

For a slightly more manual animation, see CABasicAnimation , which will update a property over time. If you need very manual control, see CADisplayLink , but you almost never need this.

In any case, you must never assume that any timer is called precisely when you ask it to be. You cannot add 0.1s to a value just because you asked to be called in 0.1s. You have to look at what time it really is. Even hard-real-time systems can't promise something will be called at a precise moment. The best you can possibly get is a promise it will be within some tolerance (and iOS doesn't even give you that).


To animate this with UIView (which I recommend), it'll probably be something like:

@IBAction func start(_ sender: Any) {
    self.clockView.currentPressure = 0
    UIView.animate(withDuration: 5, animations: {
         self.clockView.currentPressure = 1
    })
}

With a CABasicAnimation (which is more complicated) it would be something like:

currentPressure = 1 // You have to set it to where it's going or it'll snap back.
let anim = CABasicAnimation(keyPath: "currentPressure")
anim.fromValue = 0
anim.toValue = 1
anim.duration = 5
clockView.addAnimation(anim)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM