iOS,退出后台时重新启动动画

问题描述 投票:18回答:9

当我的应用程序退出后台时动画停止了。这是正常的。但我想从当前状态重新启动我的动画。如果没有我到处偷拍,我怎么做到这一点。

[UIView animateWithDuration:60 delay:0 options:(UIViewAnimationOptionCurveLinear |UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState) animations:^{
    [bg setFrame:CGRectMake(0, 0, 1378, 1005)];
} completion:nil];

我尝试在动画前放置一个设置框架,但这只是让它快速捕捉。

[bg setFrame:CGRectMake(0, 0, 1378, 1005)];

有任何想法吗?

iphone objective-c ios ipad core-animation
9个回答
26
投票

好吧,@ dany_23的答案可行。

但是我遇到了另一种方法,如果你不需要恢复动画但是重新启动动画就可以正常工作,而不需要在重新激活应用程序时进行视图或图层捕捉。

在里面

- (void)applicationWillResignActive:(UIApplication *)application

您在viewcontroller中调用一个实现以下代码的方法。

[view.layer removeAllAnimations];
 // this following CGRect is the point where your view originally started 
[bg setFrame:CGRectMake(0, 0, 1378, 1005)]; 

并在

- (void)applicationDidBecomeActive:(UIApplication *)application

你在viewcontroller中调用一个只启动动画的方法。就像是

[UIView animateWithDuration:60 delay:0 options:(UIViewAnimationOptionCurveLinear |UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState) animations:^{
      [bg setFrame:CGRectMake(0, 0, 1378, 1005)];
} completion:nil];

希望这会有所帮助,感谢所有回复的人。


27
投票

您可以在类中为UIApplicationWillEnterForegroundNotification添加一个观察者:

- (void)addNotifications { 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil]; 
}

- (void)applicationWillEnterForeground { 
    [self animate]; 
}

- (void)animate { 
    [bg setFrame:CGRectMake(0, 0, 0, 0)]; 
    [UIView animateWithDuration:60 delay:0 options:(UIViewAnimationOptionCurveLinear |UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState) animations:^{
        [bg setFrame:CGRectMake(0, 0, 1378, 1005)];
    } completion:nil];
}

设置动画的开始状态很重要(不要忘记删除通知观察者)


7
投票

这里有一个更好的解决方案,而不是每次来自背景时重新启动整个动画。

对于Swift 3,您可以继承此类:

class ViewWithPersistentAnimations : UIView {
    private var persistentAnimations: [String: CAAnimation] = [:]
    private var persistentSpeed: Float = 0.0

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.commonInit()
    }

    func commonInit() {
        NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    func didBecomeActive() {
        self.restoreAnimations(withKeys: Array(self.persistentAnimations.keys))
        self.persistentAnimations.removeAll()
        if self.persistentSpeed == 1.0 { //if layer was plaiyng before backgorund, resume it
            self.layer.resume()
        }
    }

    func willResignActive() {
        self.persistentSpeed = self.layer.speed

        self.layer.speed = 1.0 //in case layer was paused from outside, set speed to 1.0 to get all animations
        self.persistAnimations(withKeys: self.layer.animationKeys())
        self.layer.speed = self.persistentSpeed //restore original speed

        self.layer.pause()
    }

    func persistAnimations(withKeys: [String]?) {
        withKeys?.forEach({ (key) in
            if let animation = self.layer.animation(forKey: key) {
                self.persistentAnimations[key] = animation
            }
        })
    }

    func restoreAnimations(withKeys: [String]?) {
        withKeys?.forEach { key in
            if let persistentAnimation = self.persistentAnimations[key] {
                self.layer.add(persistentAnimation, forKey: key)
            }
        }
    }
}

extension CALayer {
    func pause() {
        if self.isPaused() == false {
            let pausedTime: CFTimeInterval = self.convertTime(CACurrentMediaTime(), from: nil)
            self.speed = 0.0
            self.timeOffset = pausedTime
        }
    }

    func isPaused() -> Bool {
        return self.speed == 0.0
    }

    func resume() {
        let pausedTime: CFTimeInterval = self.timeOffset
        self.speed = 1.0
        self.timeOffset = 0.0
        self.beginTime = 0.0
        let timeSincePause: CFTimeInterval = self.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
        self.beginTime = timeSincePause
    }
}

它将处理暂停当前状态下的所有动画,并在应用程序来自后台时重新添加它 - 而不重置它们。

要点:https://gist.github.com/grzegorzkrukowski/a5ed8b38bec548f9620bb95665c06128


4
投票

当您的应用进入后台时,您必须暂停动画,并在它再次变为活动状态时恢复动画。您可以找到一些示例代码here,它描述了如何暂停和恢复动画。


2
投票

好吧,如果我正确地理解了这个问题,那么你就是想创建一个动画并在中间停止它。

在这种情况下,我建议你使用某种“状态”变量来存储动画的状态(例如框架属性)。您可以在此代码中使用此变量将正在设置动画的对象放在正确的位置。这可以在animationDidStop函数(animationDidStop:finished:)中完成。为了实现此功能,您必须使用委派。它意味着将AppNameViewController(或整个应用程序的一些其他唯一对象)设置为动画的委托,并在其m文件中编写实现。这将允许您在动画结束时运行“位置设置”代码。

下一个任务是存储动画状态。在运行动画时,Core Animation框架会创建一堆中间层(表示层)。这些图层在动画期间显示,然后被删除。当动画完成执行时,对象就会进入最终状态。实际上你必须在创建所谓的“显式动画”时设置它(如果你不这样做,动画将会播放但是对象最后会跳回来)。这些表示层中的每一个都有一个集合或它自己的所有属性的副本,称为“可动画属性”。当其中一个可动画属性设置为动画的关键点时,Core Animation会调用返回YES / NO的(BOOL)needsDisplayForKey。它告诉Core Animation是否需要更改指定的键才能重新显示该层。 “重新显示”意味着为表示层调用drawInContext方法。在这里,您可以获取当前显示的表示层的属性,并将它们放入“状态”变量中。

动画以线程播放,以便设置我使用委托的“状态”变量。它需要子类CALayer(让我们说'AnimLayer)并在其中定义一个协议,只有一个存储“状态”的方法。该方法有一个参数即状态。然后我实现了在AnimLayer类中存储状态的协议方法,并将唯一对象设置为委托。因此,那些表示层(AnimLayer副本)不会自己存储状态。相反,作为参数传递给委托函数的“state”值由主线程中的唯一对象存储。

我做了类似的事,但我的任务有点不同。对不起,我不知道更简单的方法。希望这可以帮助。


2
投票

@Grzegorz Krukowski's answer对我来说就像一个魅力。我只是想补充一点,如果你不需要从它停止的地方恢复动画,但是模拟它在后台继续运行,你可以简单地省略这些行:

let timeSincePause: CFTimeInterval = self.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
self.beginTime = timeSincePause

来自resume()扩展的CALayer函数,并将beginTime设置为0.0

此外,您可以手动调用willResignActivedidBecomeActive方法,以便在ViewController消失时恢复动画,即使应用程序没有进入后台也是如此。


1
投票

这种方式会更好,只需在ApplicationDelegate上注册就会成为方法和观察者的状态。

- (void)pauseAnimate{
    CFTimeInterval pausedTime = [self.layer timeOffset];
    self.layer.speed = 1.0;
    self.layer.timeOffset = 0.0;
    self.layer.beginTime = 0.0;
    CFTimeInterval timeSincePause = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    self.layer.beginTime = timeSincePause;
}

- (void)stopAnimate{
    CFTimeInterval pausedTime = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil];
    self.layer.speed = 0.0;
    self.layer.timeOffset = pausedTime;
}

0
投票

截至发布swift5。

值得注意的是。在某些情况下,您需要重置动画属性以使新动画坚持下去。我可以通过动画转换来证实这一点。

只是打电话......

view.layer.removeAllAnimations()

不够,新动画也不会运行。我也不得不跑......

view.transform = .identity

我怀疑你可以将它设置为任何新值或保存以前的状态并进行设置。


-1
投票

我不记得确切的细节,但我认为你可以通过查看视图的图层边界和位置属性来获取框架的当前动画位置。你可以将它们存储在应用程序暂停状态,并在应用程序再次处于前台时恢复它们。

© www.soinside.com 2019 - 2024. All rights reserved.