如何防止动画在用户离开和 returns 到应用程序时完成

How to prevent an animation from completing when user leaves and returns to app

我正在制作一个应用程序,其中有一个 button 从屏幕的一侧移动到另一侧。我创建了一个暂停 button,它会在选择 button 后暂停动画。这个 button 和函数工作正常。我还添加了一个函数来确定用户是否退出了应用程序。在这个函数中,我添加了暂停button函数。然而,当用户returns退出后到应用程序时,它显示暂停屏幕,但动画已完成。这是代码:

@IBAction func pauseButton(sender: UIButton) {
    let button = self.view.viewWithTag(1) as? UIButton!
    let layer = button!.layer
    pauseLayer(layer) // Pausing the animation
    view2.hidden = false // Showing pause screen
}
func pauseLayer(layer: CALayer) {
    let pausedTime: CFTimeInterval = layer.convertTime(CACurrentMediaTime(), fromLayer: nil)
    layer.speed = 0.0
    layer.timeOffset = pausedTime
}
@IBAction func startButton(sender: UIButton) {
    let button = UIButton()
    button.tag = 1 // so I can retrieve it in the pause function
    // Extra code to make the button look nice
    button.translatesAutoresizingMaskIntoConstraints = false // Allow constraints
    let topAnchorForButton = button.topAnchor.constraintEqualToAnchor(view.topAnchor, constant: 400)
    NSLayoutConstraint.activateConstraints([ // Adding constraints
        topAnchorForButton,
        button.leadingAnchor.constraintEqualToAnchor(view.topAnchor, constant: 120),
        button.widthAnchor.constraintEqualToConstant(65),
        button.heightAnchor.constraintEqualToConstant(65)
        ])
    self.view.layoutIfNeeded()
    [UIView.animateWithDuration(5.0, delay: 0.0, options: [.CurveLinear, .AllowUserInteraction], animations: {
        topAnchorForButton.constant = 0
        self.view.layoutIfNeeded()
        }, completion: nil )] // the animation

override func viewDidLoad() {
    super.viewDidLoad()
         NSNotificationCenter.defaultCenter().addObserver(self, selector: "pauseButton:", name: UIApplicationWillResignActiveNotification, object: nil) // When exists app
         NSNotificationCenter.defaultCenter().addObserver(self, selector: "pauseButton:", name: UIApplicationDidEnterBackgroundNotification, object: nil) // when goes to home screen
}

当我重新进入应用程序时,动画已完成。

这是我试过的代码,它只播放和停止动画。我还在 App Delegate 中添加了一些东西,但它仍然不起作用:

ViewController:

class Home: UIViewController {

func pauseAnimation(){
let button = self.view.viewWithTag(1) as? UIButton!
let layer = button!.layer
pauseLayer(layer) // Pausing the animation
}  
@IBAction func pauseButton(sender: UIButton) {
   pauseAnimation()
}
func pauseLayer(layer: CALayer) {
    let pausedTime: CFTimeInterval = layer.convertTime(CACurrentMediaTime(), fromLayer: nil)
    layer.speed = 0.0
    layer.timeOffset = pausedTime
}
@IBAction func startButton(sender: UIButton) {
    let button = UIButton()
    button.tag = 1 // so I can retrieve it in the pause function
    // Extra code to make the button look nice
    button.setTitle("Moving", forState: .Normal)
    button.setTitleColor(UIColor.redColor(), forState: .Normal)
    view.addSubview(button)
    button.translatesAutoresizingMaskIntoConstraints = false // Allow constraints
    let topAnchorForButton = button.topAnchor.constraintEqualToAnchor(view.topAnchor, constant: 400)
    NSLayoutConstraint.activateConstraints([ // Adding constraints
        topAnchorForButton,
        button.leadingAnchor.constraintEqualToAnchor(view.topAnchor, constant: 120),
        button.widthAnchor.constraintEqualToConstant(65),
        button.heightAnchor.constraintEqualToConstant(65)
        ])
    self.view.layoutIfNeeded()
    [UIView.animateWithDuration(10.0, delay: 0.0, options: [.CurveLinear, .AllowUserInteraction], animations: {
        topAnchorForButton.constant = 0
        self.view.layoutIfNeeded()
        }, completion: nil )] // the animation
}

应用程序代表:

class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?
var vc = Home()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
}
func applicationWillResignActive(application: UIApplication) {
   vc.pauseAnimation()
}

以防万一你想知道,这里是恢复动画的代码:

@IBAction func resume(sender: UIButton) {
    let button = self.view.viewWithTag(100) as? UIButton!
    let layer = button!.layer 
    resumeLayer(layer)
}
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
}

当我退出应用程序时,它说 button or/and layer is nil (fatal error... optional value is nil),所以它崩溃了。

请帮忙。提前致谢...安东

尝试将此代码放入:

视图控制器:

pauseAnimation(){

    let button = self.view.viewWithTag(1) as? UIButton!
let layer = button!.layer
pauseLayer(layer) // Pausing the animation
view2.hidden = false // Showing pause screen

}

AppDelegate:

func applicationWillResignActive(application: UIApplication) {

    vc.pauseAnimation()

}

要能够从 appDelegate 中的视图控制器访问功能,请转到此处:link

看看here。清单 5-4 显示了如何暂停和恢复动画的示例。您应该将代码移出 AppDelegate,取而代之的是视图控制器 (Home) 应该处理进出后台。在首页 viewDidLoadviewWillAppear:

NSNotificationCenter.defaultCenter().addObserver(
    self,
    selector: "resigningActive",
    name: UIApplicationWillResignActiveNotification,
    object: nil
)
NSNotificationCenter.defaultCenter().addObserver(
    self,
    selector: "becomeActive",
    name: UIApplicationDidBecomeActiveNotification,
    object: nil
)

您将需要 removeObserver,可能在 Home 的 deinit 中。那么你有:

func resigningActive() {
    pauseLayer(button!.layer)
}
func becomeActive() {
    resumeLayer(button!.layer)
}

此代码不需要 pause/resume 的另一个按钮,但如果您愿意,也可以这样做。您可能需要在 viewWillAppear 中定义观察者,因为看起来您的代码将引用 viewDidLoad 中尚不存在的出口。这意味着您必须编写代码以仅在第一次调用 viewWillAppear 时连接观察者,否则您将为同一事件添加多个观察者。

您的 pauseLayer 还应该将 pausedTime 写入 NSUserDefaults,并且 resumeLayer 应该按照(未测试)的方式将其读回:

// Write
NSUserDefaults.standarduserDefaults().setDouble(Double(pausedTime), forKey: "pausedTime")
// Read
pausedTime = CFTimeInterval(NSUserDefaults.standarduserDefaults().doubleForKey("pausedTime"))

您需要了解一些动画的底层工作原理。这在 WWDC session 236 'Building Interruptible and Responsive Interactions' 中有很好的解释(您感兴趣的部分从 17:45 开始)。同时,阅读 CABasicAnimation,并查看 CALayer 上的 presentationLayer 属性(您需要下拉至 CALayer 以获得您想要实现的目标,也许您需要使用 CA(Basic)Animation 来实现动画,但如果您的代码结构良好,UIKit 的动画方法也许仍然可以解决问题。

基本上,视图或图层会立即获得您设置为动画一部分的 end-values。然后动画开始,从头到尾。如果动画被打断,比如离开应用程序,在 return 时,视图将使用它具有的值(即 end-values,因为动画消失)绘制。

你想要的是当你离开应用程序时在动画中的那个点捕获视图的值,保存它,并在你的应用程序返回时从那个点到原始端点开始一个新的动画前景。您可以通过从 CALayerpresentationLayer 属性.

我在构建用于任何 `UIScrollView`` 的自定义 refreshControl 时遇到了这个问题。这段代码在 Github 上,你的相关代码在这里(它的 Objective-C,但它不是很复杂,所以你应该能够跟着 re-implement 你需要的Swift): JRTRefreshControlView.m,具体来说:

-(void)willMoveToWindow:(UIWindow *)newWindow 当用户将应用程序移至后台时,或者当视图从 superView 中移除时(newWindow 将为 nil),此函数将被调用。

而子类中动画的实际实现在这里: JRTRotatingRefreshControlView.m,特别是

- (void) resumeRefreshingState方法就是你要看的

(还有 saveRefreshingState,你可以从 presentationLayer 中保存 presentation-values,但我的动画没有实现)。