如何从多任务处理回来后恢复CAAnimation

时间:2011-07-22 15:59:11

标签: background core-animation calayer multitasking caanimation

在我的应用程序中,我有一个CALayer数组,我在bezierPath上进行了动画处理。当我关闭并重新打开应用程序时,我的图层不是动画,而是与关闭应用程序之前的位置不同。我已经实现了两个方法,pauseLayer和resumeLayer,当我在我的应用程序中使用两个按钮触发它们时它们可以工作,但是在关闭应用程序后它们将无法工作。代码如下

   - (void)pauseLayers{

    for(int y=0; y<=end;y++)
    {



        CFTimeInterval pausedTime = [car[y] convertTime:CACurrentMediaTime() fromLayer:nil];
        car[y].speed = 0.0;
        car[y].timeOffset = pausedTime;

         standardUserDefaults[y] = [NSUserDefaults standardUserDefaults];


        if (standardUserDefaults[y]) {
            [standardUserDefaults[y] setDouble:pausedTime forKey:@"pausedTime"];
            [standardUserDefaults[y] synchronize];
        }


        NSLog(@"saving positions");


        }


}

-(void)resumeLayers

{  




    for(int y=0; y<=end;y++)
    {




        standardUserDefaults[y] = [NSUserDefaults standardUserDefaults];     
        car[y].timeOffset = [standardUserDefaults[y] doubleForKey:@"pausedTime"];

    CFTimeInterval pausedTime = [car[y] timeOffset];
    car[y].speed = 1.0;
    car[y].timeOffset = 0.0;
    car[y].beginTime = 0.0;

    CFTimeInterval timeSincePause = [car[y] convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    car[y].beginTime = timeSincePause;
        }


}

4 个答案:

答案 0 :(得分:3)

- (void)applicationDidEnterBackground:(UIApplication *)application {

 mosquitosViewController *mvc = [[mosquitosViewController alloc] init];
  [mvc pauseLayers];

  }

您在上面尝试执行的操作的问题是您正在创建一个全新的视图控制器实例,而不是屏幕上显示的实例。这就是发送pauseLayers消息时没有任何反应的原因。

您应该注册以接收有关您的应用何时进入并来自后台的通知,并在该通知到达时调用相应的方法(pauseLayersresumeLayers)。

您应该在mosquitosViewController实现中的某处添加以下代码(我通常在viewDidLoad中执行此操作):

// Register for notification that app did enter background
[[NSNotificationCenter defaultCenter] addObserver:self 
                                      selector:@selector(pauseLayers)
                                      name:UIApplicationDidEnterBackgroundNotification 
                                      object:[UIApplication sharedApplication]];

// Register for notification that app did enter foreground
[[NSNotificationCenter defaultCenter] addObserver:self 
                                      selector:@selector(resumeLayers) 
                                      name:UIApplicationWillEnterForegroundNotification 
                                      object:[UIApplication sharedApplication]];

答案 1 :(得分:0)

- (void)applicationDidEnterBackground:(UIApplication *)application

{

NSLog(@"1");
mosquitosViewController *mvc = [[mosquitosViewController alloc] init];
[mvc pauseLayers];

/*
 Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
 If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
 */

}

答案 2 :(得分:0)

有关如何在多任务处理后重新启动动画的详细信息,请参阅此帖子的回答:

Restoring animation where it left off when app resumes from background

答案 3 :(得分:0)

我基于@cclogg和来自this thread的@Matej Bukovinski答案编写了 Swift 4 版本扩展。您只需拨打layer.makeAnimationsPersistent()

即可

全部要点:CALayer+AnimationPlayback.swift, CALayer+PersistentAnimations.swift

核心部分:

public extension CALayer {
    static private var persistentHelperKey = "CALayer.LayerPersistentHelper"

    public func makeAnimationsPersistent() {
        var object = objc_getAssociatedObject(self, &CALayer.persistentHelperKey)
        if object == nil {
            object = LayerPersistentHelper(with: self)
            let nonatomic = objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
            objc_setAssociatedObject(self, &CALayer.persistentHelperKey, object, nonatomic)
        }
    }
}

public class LayerPersistentHelper {
    private var persistentAnimations: [String: CAAnimation] = [:]
    private var persistentSpeed: Float = 0.0
    private weak var layer: CALayer?

    public init(with layer: CALayer) {
        self.layer = layer
        addNotificationObservers()
    }

    deinit {
        removeNotificationObservers()
    }
}

private extension LayerPersistentHelper {
    func addNotificationObservers() {
        let center = NotificationCenter.default
        let enterForeground = NSNotification.Name.UIApplicationWillEnterForeground
        let enterBackground = NSNotification.Name.UIApplicationDidEnterBackground
        center.addObserver(self, selector: #selector(didBecomeActive), name: enterForeground, object: nil)
        center.addObserver(self, selector: #selector(willResignActive), name: enterBackground, object: nil)
    }

    func removeNotificationObservers() {
        NotificationCenter.default.removeObserver(self)
    }

    func persistAnimations(with keys: [String]?) {
        guard let layer = self.layer else { return }
        keys?.forEach { (key) in
            if let animation = layer.animation(forKey: key) {
                persistentAnimations[key] = animation
            }
        }
    }

    func restoreAnimations(with keys: [String]?) {
        guard let layer = self.layer else { return }
        keys?.forEach { (key) in
            if let animation = persistentAnimations[key] {
                layer.add(animation, forKey: key)
            }
        }
    }
}

@objc extension LayerPersistentHelper {
    func didBecomeActive() {
        guard let layer = self.layer else { return }
        restoreAnimations(with: Array(persistentAnimations.keys))
        persistentAnimations.removeAll()
        if persistentSpeed == 1.0 { // if layer was playing before background, resume it
            layer.resumeAnimations()
        }
    }

    func willResignActive() {
        guard let layer = self.layer else { return }
        persistentSpeed = layer.speed
        layer.speed = 1.0 // in case layer was paused from outside, set speed to 1.0 to get all animations
        persistAnimations(with: layer.animationKeys())
        layer.speed = persistentSpeed // restore original speed
        layer.pauseAnimations()
    }
}