2016-09-17 59 views
0

问:暂停与恢复的UIView动画与Alpha当用户按下Home键不起作用

  1. 如何暂停嵌套的UIView动画以动画的α/阻当用户按下主页按钮并返回到应用程序?
  2. 我在下面的代码中错过了什么?谢谢。

背景:

我有一个简单的嵌套的UIView动画在不同的时间缓慢地调整的图的α从alpha = 0alpha = 1超过1分钟。

当用户按下主页按钮时,UIView动画不会暂停,因此当应用程序恢复时,UIView动画快进到动画的最后,alpha在恢复应用程序时立即从0变为1 。

所以,我试图暂停UIView动画,当用户按下设备的主页按钮并暂时挂起应用程序。

我有一个函数暂停动画(pauseLayer),然后重新开始动画(resumeLayer)。从UIButton拨打电话时,这两个功能非常有用。它暂停alpha并按预期恢复动画。 (然而,如果被按压主页按钮,而动画暂停时,当它恢复所述α的变化瞬间从0到1)

当我尝试调用pauseLayer当用户按压主页按钮(接收WillResignActive通知),然后返回到应用程序(接收到WillEnterForeground通知),则动画不会暂停并继续,而是在恢复应用程序时alpha会立即从0更改为1。

它似乎应该工作,但事实并非如此。


代码:

import UIKit 

class ViewController: UIViewController { 

    @IBOutlet weak var myView1: UIView! 
    @IBOutlet weak var myView2: UIView! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     setNotifications() 
     myAnimation() 
    } 

    @IBAction func myPauseButton(sender: UIButton) { 
     let layer = self.view.layer 
     pauseLayer(layer) 
    } 

    @IBAction func myResumeButton(sender: UIButton) { 
     let layer = self.view.layer 
     resumeLayer(layer) 
    } 

    func setNotifications() { 
     NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.WillEnterForeground(_:)), name: UIApplicationWillEnterForegroundNotification, object: nil) 

     NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.WillResignActive(_:)), name: UIApplicationWillResignActiveNotification, object: nil) 
    } 

    func WillEnterForeground(notification : NSNotification) { 
     let layer = self.view.layer 
     resumeLayer(layer) 
    } 

    func WillResignActive(notification : NSNotification) { 
     let layer = self.view.layer 
     pauseLayer(layer) 
    } 

    func pauseLayer(layer: CALayer) { 
     let pausedTime: CFTimeInterval = layer.convertTime(CACurrentMediaTime(), fromLayer: nil) 
     layer.speed = 0.0 
     layer.timeOffset = pausedTime 
    } 

    func resumeLayer(layer: CALayer) { 
     let pausedTime: CFTimeInterval = layer.timeOffset 
     layer.speed = 1.0 
     layer.timeOffset = 0.0 
     layer.beginTime = 0.0 
     let timeSincePause: CFTimeInterval = layer.convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime 
     layer.beginTime = timeSincePause 
    } 

    func myAnimation() { 
     self.myView1.alpha = 0 
     UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut], animations: { 
      self.myView1.alpha = 0.1 
      }, completion: {(finished: Bool) in 
       UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut], animations: { 
        self.myView1.alpha = 0.2 
        }, completion: {(finished: Bool) in 
         UIView.animateWithDuration(30, delay: 0, options: [.CurveEaseInOut], animations: { 
          self.myView1.alpha = 1 
          }, completion: nil) 
       }) 
     }) 

     self.myView2.alpha = 0 
     UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut], animations: { 
      self.myView2.alpha = 0.1 
      }, completion: {(finished: Bool) in 
       UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut], animations: { 
        self.myView2.alpha = 0.2 
        }, completion: {(finished: Bool) in 
         UIView.animateWithDuration(30, delay: 0, options: [.CurveEaseInOut], animations: { 
          self.myView2.alpha = 1 
          }, completion: nil) 
       }) 
     }) 
    } 

} 

编辑:

如果我添加if (finished) {到每个部分,然后按Home键,然后返回到应用程序中,动画只进行到下一个部分并停止,不再进一步。这样比较好,但是问题在于resumeLayer似乎不起作用,所以动画保持停止状态,不会继续。

 self.myView2.alpha = 0 
     UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut, .LayoutSubviews, .AllowUserInteraction, .BeginFromCurrentState], animations: { 
      self.myView2.alpha = 0.1 
      }, completion: {(finished: Bool) in 
       if (finished) { 
       UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut, .LayoutSubviews, .AllowUserInteraction, .BeginFromCurrentState], animations: { 
        self.myView2.alpha = 0.2 
        }, completion: {(finished: Bool) in 
         if (finished) { 
         UIView.animateWithDuration(30, delay: 0, options: [.CurveEaseInOut, .LayoutSubviews, .AllowUserInteraction, .BeginFromCurrentState], animations: { 
          self.myView2.alpha = 1 
          }, completion: nil) 
         } 
       }) 
       } 
     }) 
+0

也许不是UIApplicationWillEnterForeground的,你应该使用UIApplicationDidBecomeActive – DerrickHo328

+0

不幸的是,没有工作@ Calimari328但感谢,虽然,我没有尝试。 – user4806509

回答

1

您不需要使用UIView动画来完成简单的淡入淡出。

您可以使用Timer对象和一些Math来手动设置它。

class ViewController: UIViewController {   
    override func viewDidLoad() { 
     super.viewDidLoad() 
     view.backgroundColor = colorScheme.red 

     NotificationCenter.default.addObserver(self, selector: #selector(self.didBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil) 
     NotificationCenter.default.addObserver(self, selector: #selector(self.didResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil) 
    } 

    func didBecomeActive() { 
     if case min_t..<max_t = t { 
      beginAnimation(at: t) 
     } 
    } 

    func didResignActive() { 
     _ = pauseAnimation() 
    } 


    var t = TimeInterval(0.0) 
    let min_t = TimeInterval(0) 
    let max_t = TimeInterval(100) 
    let delta = TimeInterval(0.1) 
    var timerObj: Timer? 

    func timer(_ aTimer: Timer) { 
     if t < max_t { 
      // Using a parabola 
      view.alpha = CGFloat(0.0001 * t * t) 
     } else { 
      _ = pauseAnimation() 
     } 
     t = t + 1 
    } 

    func beginAnimation(at t: TimeInterval = 0) { 
     self.t = t 
     timerObj = Timer.scheduledTimer(timeInterval: delta, target: self, selector: #selector(self.timer), userInfo: nil, repeats: true) 
    } 

    func pauseAnimation() -> TimeInterval { 
     timerObj?.invalidate() 
     return t 
    } 

    override func viewDidAppear(_ animated: Bool) { 
     beginAnimation() 
    } 
} 

但是,有可能您的应用程序实际上更复杂,您的示例是一个简化。那么,这个怎么样?

class ViewController: UIViewController { 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     view.backgroundColor = .red 

     NotificationCenter.default.addObserver(self, selector: #selector(self.WillEnterForeground(notification:)), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil) 
     NotificationCenter.default.addObserver(self, selector: #selector(self.WillResignActive(notification:)), name: NSNotification.Name.UIApplicationWillResignActive, object: nil) 
    } 

    func WillEnterForeground(notification : NSNotification) { 
     if let timegap = endDate?.timeIntervalSince(startDate) 
      , timegap < duration { 
      beginAnimation(with: timegap/duration) 
     } 
    } 

    func WillResignActive(notification : NSNotification) { 
     let layer = self.view.layer 
     layer.removeAllAnimations() 
    } 

    var duration = TimeInterval(10) 
    var percentComplete = 0.0 
    var startDate: Date = Date() 
    var endDate: Date? 

    func beginAnimation(with percent: Double = 0.0) { 
     view.alpha = CGFloat(percent) 

     startDate = Date() 
     UIView.animateKeyframes(withDuration: duration * (1 - percent) 
      , delay: 0, options: UIViewKeyframeAnimationOptions.calculationModeLinear, 
       animations: { 
       if percent < 0.2 { 
        UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.2, animations: { 
         self.view.alpha = 0.1 
        }) 
       } 
       if percent < 0.5 { 
        UIView.addKeyframe(withRelativeStartTime: 0.2, relativeDuration: 0.3, animations: { 
         self.view.alpha = 0.2 
        }) 
       } 

       UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.7, animations: { 
        self.view.alpha = 1.0 
       }) 
     }) { (b: Bool) in 
      if b == false { 
       self.endDate = Date() 
       if let timegap = self.endDate?.timeIntervalSince(self.startDate) 
        , timegap < self.duration { 
        self.view.alpha = CGFloat(timegap/self.duration) 
       } 
      } 
     } 
    } 

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

} 

主要想法是获取动画开始前的时间和结束时间。如果动画成功,完成会告诉我们,但如果用户按下主页按钮,完成块会告诉我们错误。如果它是错误的,我们可以捕获日期并找出时间差异并计算完成百分比。那么我们可以使用百分比来确定剩下的时间量。

希望帮助

+0

这给出了一些想法。谢谢。我希望保留'UIView.animateWithDuration',因为关键帧动画有时可能会受到限制。当按下按钮时,原始方法起作用,但将应用程序置于后台将不幸中断该方法。你是对的,这里有一个比这更精致的动画。然而,这个简洁的版本引发了与alpha动画完全相同的问题。这里的简要例子是保持一切都很简单。我不明白的是为什么动画在每个部分都没有完成时会在后台快速转发? – user4806509