2016-09-17 4 views
0

質問:一時停止とアルファでUIViewのアニメーションを再開は動作しません、ユーザーがホームボタンを押す

  1. ユーザーがホームを押したときにどのように私は、アルファ/不透明度をアニメーション化し、ネストされたUIViewのアニメーションを一時停止することができますボタンを押してアプリに戻りますか?
  2. 以下のコードで何が欠けていますか?ありがとうございました。

背景:

私はalpha = 0からalpha = 1 1分以上に異なる時間にゆっくりとビューのアルファを調整し、単純なネストされたUIViewのアニメーションを持っています。

ユーザーがホームボタンを押すと、UIViewアニメーションが一時停止しないため、アプリが再開すると、UIViewアニメーションがアニメーションの最後まで早送りされ、アプリを再開するとアルファが即座に0から1に変更されます。

ユーザーがデバイスの[ホーム]ボタンを押してアプリケーションを一時停止すると、UIViewアニメーションを一時停止しようとしています。

アニメーションを一時停止して(pauseLayer)、アニメーションを再開する機能(resumeLayer)があります。これらの2つの機能は、UIButtonから呼び出すときに効果的です。 alphaを一時停止し、期待通りにアニメーションを再開します。 (ホームボタンが押された場合、それは0から1へ即座アルファ変更を再開したときただし、アニメーションが、一時停止している間)

私は、ユーザーがWillResignActive通知を受けたホームボタンを(押したときpauseLayerを呼び出すしようとすると)、その後、アプリに戻ります(WillEnterForeground通知を受け取ると)、アニメーションは一時停止せずに再開します。代わりに、アプリを再開するとアルファが即座に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) 
       }) 
     }) 
    } 

} 

EDIT:

私は、各セクションにif (finished) {を追加する場合は、アプリに戻った後、ホームボタンを押して、アニメーションは次のセクションに進み、停止します。これはより優れていますが、その問題は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あなたはCalimari328 @動作しませんでしたが、おかげで、しかし、私はそれを試していませんでした。その残念なことにUIApplicationDidBecomeActive – DerrickHo328

+0

を使用する必要がありますが役に立てば幸い – 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() 
    } 

} 

主なアイデアは、アニメーションが開始する前の時間と終了する時間を取得することです。完了はアニメーションが成功したかどうかを示しますが、ユーザがホームボタンを押した場合、完了ブロックはfalseを示します。 falseの場合は、日付を取得して時差を見つけ、完了率を計算することができます。その割合を使用して、残り時間を判断することができます。

+0

これはいくつかのアイデアを提供します。ありがとう。私は、キーフレームアニメーションが時々制限される可能性があるので、「UIView.animateWithDuration」を保持したいと考えていました。元のメソッドは、ボタンが押されたときに機能しますが、アプリケーションをバックグラウンドに置くと、残念なことにそのメソッドが中断されます。あなたは正しいです、ここに置いたものよりも精巧なアニメーションがあります。しかし、この簡潔なバージョンは、アルファアニメーションに関してまったく同じ問題を投げます。ここで簡潔に示す例は、それをすべて単純に保つことです。私が理解していないのは、なぜ各部分が完了していないときに、アニメーションがバックグラウンドで早送りするのかです。 – user4806509

関連する問題