2016-09-17 62 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