当用户按下Home按钮时,使用alpha暂停和恢复UIView动画不起作用

时间:2016-09-17 21:10:46

标签: ios swift animation uiview uiviewanimation

问题:

  
      
  1. 当用户按下“主页”按钮并返回应用程序时,如何暂停嵌套的UIView动画以激活alpha /不透明度?
  2.   
  3. 我在下面的代码中遗漏了什么?谢谢。
  4.   

背景

我有一个简单的嵌套UIView动画,可以在1分钟内从alpha = 0alpha = 1的不同时间缓慢调整视图的alpha值。

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

因此,当用户按下设备主页按钮并暂时暂停应用程序时,我正在尝试暂停UIView动画。

我有一个暂停动画(pauseLayer)的功能,然后重新开始动画(resumeLayer)。从UIButton调用它们时,这两个函数的效果很好。它会暂停alpha并按预期恢复动画。 (但是,如果在动画暂停时按下主页按钮,则当它恢复时,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) {添加到每个部分,然后按主页按钮,然后返回到应用程序,动画只会进入下一部分并停止,不再进一步。这样更好,但问题是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)
                        }
                })
                }
        })

1 个答案:

答案 0 :(得分:1)

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

您可以使用Timer对象和一个小数学来手动设置动画。

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

}

主要思想是在动画开始之前获取时间以及结束时间。完成将告诉我们动画是否成功,但如果用户按下主页按钮,完成块将告诉我们错误。如果它是假的,我们可以捕获日期并找出时差并计算完成百分比。然后我们可以用这个百分比来确定剩下的时间。

希望有所帮助