0
votes

I have an imageView and want it to rotate 360° all the time, but I found an issue which is that when the App enters background and then back to the foreground, the rotate animation will be stopped. And I don't want to re-call the function rotate360Degree() when the app back to the foreground, the reason is that I want the rotate-animation will start at the position where it left when entering background, instead of rotating from 0 again. But when I call the function resumeRotate(), it doesn't work.

The extension as follow:

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()


    }

}

Here is the layer Extension :

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
    }
}

I can solve the above 'stopped' issue with CADisplayLink, but the animation will not rotate from the position where it left(rotate all the time). I wonder how to solve it with CADisplayLink? And how with the above core animation?

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)

    }
1

1 Answers

0
votes

You can do this with UIKit's higher-level block based animation api. If you want a continuously rotating view with 20.0 second duration. You can with a function like:

    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
            })
        })
    }

This will just rotate the view by 180 degrees then once that is complete rotate it another 180 for 360 degree rotation. The .repeat option will cause it to repeat indefinitely. However, the animation will stop when the app is backgrounded. For that, we need to save the state of the presentation layer of the view being rotated. We can do that by storing the CGAffineTransform of the presentation layer and then setting that transform to the view being animated when the app comes back into the foreground. Here's an example with a 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
            })
        })
    }
}

Which results in this:

enter image description here