当应用程序回到前台时如何恢复核心动画

How to resume core animation when the app back to foreground

我有一个imageView,想让它一直旋转360°,但是我发现了一个问题,就是当App进入background然后回到foreground时,旋转动画将停止。 而且我不想在应用程序回到前台时重新调用函数 rotate360Degree() ,原因是我希望旋转动画将从进入后台时离开的位置开始,而不是旋转又从0开始。 但是当我调用函数resumeRotate()时,它不起作用。

扩展名如下:

extension UIImageView {
    // 360度旋转图片
    func rotate360Degree() {
        let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z") // 让其在z轴旋转
        rotationAnimation.toValue = NSNumber(value: .pi * 2.0) // 旋转角度
        rotationAnimation.duration = 20 // 旋转周期
        rotationAnimation.isCumulative = true // 旋转累加角度
        rotationAnimation.repeatCount = MAXFLOAT // 旋转次数
        rotationAnimation.autoreverses = false
        layer.add(rotationAnimation, forKey: "rotationAnimation")
    }

    // 暂停旋转
    func pauseRotate() {

        layer.pauseAnimation()
    }

    // 恢复旋转
    func resumeRotate() {

        layer.resumeAnimation()


    }

}

这是图层扩展:

var pauseTime:CFTimeInterval!

extension CALayer {
    //暂停动画
    func pauseAnimation() {
        pauseTime = convertTime(CACurrentMediaTime(), from: nil)
        speed = 0.0
        timeOffset = pauseTime
    }
    //恢复动画
    func resumeAnimation() {
        // 1.取出时间
         pauseTime = timeOffset
        // 2.设置动画的属性
        speed = 1.0
        timeOffset = 0.0
        beginTime = 0.0
        // 3.设置开始动画
        let startTime = convertTime(CACurrentMediaTime(), from: nil) - pauseTime
        beginTime = startTime
    }
}

我可以用CADisplayLink解决上面的'stopped'问题,但是动画不会从离开的位置开始旋转(一直旋转)。 请问如何用CADisplayLink解决? 以及如何搭配上面的核心动画?

displayLink = CADisplayLink(target: self, selector: #selector(rotateImage))
displayLink.add(to: .current, forMode: .commonModes)

 func rotateImage(){

         let angle =  CGFloat(displayLink.duration * Double.pi / 18)

         artworkImageView.transform = artworkImageView.transform.rotated(by: angle)

    }

您可以使用 UIKit 的更高级别的基于块的动画 api 来完成此操作。如果你想要一个持续时间为 20.0 秒的连续旋转视图。您可以使用如下函数:

    func animateImageView()
    {
        UIView.animate(withDuration: 10.0, delay: 0.0, options: [.beginFromCurrentState, .repeat, .curveLinear], animations:
        { [unowned self] in
            var transform = self.imageView.transform
            transform = transform.concatenating(CGAffineTransform(rotationAngle: CGFloat.pi))
            self.imageView.transform = transform
        },
        completion:
        { _ in
            UIView.animate(withDuration: 10.0, delay: 0.0, options: [.beginFromCurrentState, .curveLinear], animations:
            { [unowned self] in
                var transform = self.imageView.transform
                transform = transform.concatenating(CGAffineTransform(rotationAngle: CGFloat.pi / 2.0))
                self.imageView.transform = transform
            })
        })
    }

这只会将视图旋转 180 度,完成后再旋转 180 度,实现 360 度旋转。 .repeat 选项将导致它无限期地重复。但是,当应用程序进入后台时,动画将停止。为此,我们需要保存正在旋转的视图的表示层的状态。我们可以通过存储表示层的 CGAffineTransform 然后将该转换设置为当应用程序返回前台时正在动画的视图来实现。这是一个 UIImageView

的例子
class ViewController: UIViewController
{

    @IBOutlet weak var imageView: UIImageView!
    var imageViewTransform = CGAffineTransform.identity

    override func viewDidLoad()
    {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(self.didEnterBackground), name: .UIApplicationDidEnterBackground, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.willEnterForeground), name: .UIApplicationWillEnterForeground, object: nil)
    }

    override func viewDidAppear(_ animated: Bool)
    {
        super.viewDidAppear(animated)
        animateImageView()
    }

    deinit
    {
        NotificationCenter.default.removeObserver(self)
    }

    func didEnterBackground()
    {
        imageViewTransform = imageView.layer.presentation()?.affineTransform() ?? .identity
    }

    func willEnterForeground()
    {
        imageView.transform = imageViewTransform
        animateImageView()
    }

    func animateImageView()
    {
        UIView.animate(withDuration: 10.0, delay: 0.0, options: [.beginFromCurrentState, .repeat, .curveLinear], animations:
        { [unowned self] in
            var transform = self.imageView.transform
            transform = transform.concatenating(CGAffineTransform(rotationAngle: CGFloat.pi))
            self.imageView.transform = transform
        },
        completion:
        { _ in
            UIView.animate(withDuration: 10.0, delay: 0.0, options: [.beginFromCurrentState, .curveLinear], animations:
            { [unowned self] in
                var transform = self.imageView.transform
                transform = transform.concatenating(CGAffineTransform(rotationAngle: CGFloat.pi / 2.0))
                self.imageView.transform = transform
            })
        })
    }
}

结果是: