iOS 应用程序中带有计时器和循环的流畅动画

moo*_*der 0 animation timer grand-central-dispatch swift

我的 ViewController 的星级评级如下所示(除了有 10 颗星)

在此输入图像描述

当用户打开 ViewController 来查看某些没有评级的对象时,我想用非常简单的方法将用户的注意力吸引到这些星星上:动画星星突出显示(当每个字母相继突出显示时,您可以在现实世界中的某些广告上看到这种行为)。

  1. 突出显示一颗星
  2. 突出显示两颗星
  3. 突出显示三颗星
  4. ......
  5. 全部关闭

这就是我的做法

func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) {
    DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
        completion()
    }
} 

func ratingStarsAnimation() {
    for i in 1...11 {
        var timer : Double = 0.6 + Double(i)*0.12
        delayWithSeconds(timer) {
            ratingStars.rating = (i < 10) ? Double(i) : 0
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?我有一个名为delayWithSeconds 的函数来延迟操作,我使用这个函数来延迟每个星星的突出显示。0.6 是动画开始前的初始延迟。突出显示所有星星后 - 最后一步是关闭所有星星的突出显示。这段代码可以工作,但我不能说它很顺利。

我的问题是:

  1. 如何更改 0.6 + Double(i)*0.12 以获得平滑的动画感觉?
  2. 我认为我的延迟解决方案不好 - 我怎样才能更好地解决平滑的星星突出显示任务?

小智 5

Have a look at the CADisplaylink class. Its a specialized timer that is linked to the refresh rate of the screen, on iOS this is 60fps. It's the backbone of many 3rd party animation libraries.

Usage example:

var displayLink: CADisplayLink?
let start: Double = 0
let end: Double = 10
let duration: CFTimeInterval = 5 // seconds
var startTime: CFTimeInterval = 0

let ratingStars = RatingView()

func create() {
    displayLink = CADisplayLink(target: self, selector: #selector(tick))
    displayLink?.add(to: .main, forMode: .defaultRunLoopMode)
}

func tick() {
    guard let link = displayLink else {
        cleanup()
        return
    }

    if startTime == 0 { // first tick
        startTime = link.timestamp
        return
    }

    let maxTime = startTime + duration
    let currentTime = link.timestamp

    guard currentTime < maxTime else {
        finish()
        return
    }

    // Add math here to ease the animation

    let progress = (currentTime - startTime) / duration
    let progressInterval = (end - start) * Double(progress)

    // get value =~ 0...10
    let normalizedProgress = start + progressInterval


    ratingStars.rating = normalizedProgress
}

func finish() {
    ratingStars.rating = 0
    cleanup()
}

func cleanup() {
    displayLink?.remove(from: .main, forMode: .defaultRunLoopMode)
    displayLink = nil
    startTime = 0
}
Run Code Online (Sandbox Code Playgroud)

As a start this will allow your animation to be smoother. You will still need to add some trigonometry if you want to add easing but that shouldn't be too difficult.