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

问题描述:

我有一个imageView,并希望它始终旋转360°,但我发现的问题是,当应用程序进入background,然后回到​​,旋转动画将停止。 而我不想在应用程序回到前台时重新调用函数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,但动画将不会从旋转它离开的位置(始终旋转)。 我想知道如何用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的360度旋转而旋转,然后通过180度视图。 .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 
      }) 
     }) 
    } 
} 

导致此:

enter image description here

+0

@Hi beyowulf。感谢您的详细回复。我会尝试解决您的问题。但问题是持续时间不适用于后台或前台应用程序。它只涉及用户的行为。例如,该应用程序是音乐播放器,imageView用于音乐作品,用户可以用后台模式听音乐30分钟,2小时或更长时间。 – Ringo

+0

我不明白你的意见。应用程序在后台的持续时间可以是任何无关紧要,只要应用程序停留在内存中即可。我认为问题是如何为视图设置动画,以便在应用程序背景时恢复动画,继续播放,就好像它从未背景一样。情况并非如此吗? – beyowulf

+0

哦,对不起,我误解了你的代码中的数字10。这是动画的持续时间,而不是应用程序在后台的时间。 – Ringo