iOS, Restarting animation when coming out of the b

2019-01-17 11:48发布

when my app comes out of the background the animation has stopped. which is normal. but i want to restart my animation from the current state. how do i do that without my snapping all over the place.

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

i tried putting a set frame in front of the animation but that just makes it snap.

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

any ideas?

7条回答
一夜七次
2楼-- · 2019-01-17 12:10

well the answer of @dany_23 could work.

But I came across an other method that works just fine if you don't need to resume your animation but restart your animation, without the view or layer snapping when you reactivate the app.

in the

- (void)applicationWillResignActive:(UIApplication *)application

you call a method in your viewcontroller which implements the following code.

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

and in the

- (void)applicationDidBecomeActive:(UIApplication *)application

you call a method in your viewcontroller that just starts the animation. something like

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

Hope this helps, Thanks to all who replied.

查看更多
虎瘦雄心在
3楼-- · 2019-01-17 12:12

You can add an observer in your class for 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];
}

It is important to set the begin state of the animation (and don't forget to remove the notification observer)

查看更多
【Aperson】
4楼-- · 2019-01-17 12:13

You'll have to pause the animation when your app goes into the background and resume it when it becomes active again. You can find some sample code here that describes how to pause and resume an animation.

查看更多
一纸荒年 Trace。
5楼-- · 2019-01-17 12:15

I don't recall the exact details but I think you can get the current animated position of the frame by looking at the layer bounds and position property of your view. You could store those at app suspend and restore them when the app is in the foreground again.

查看更多
做个烂人
6楼-- · 2019-01-17 12:21

This way will be better, just register on ApplicationDelegate will become methods and observer the status.

- (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;
}
查看更多
一纸荒年 Trace。
7楼-- · 2019-01-17 12:25

There is a better soloution here than restarting whole animation each time you come from background.

For Swift 3 you can subclass this class:

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

It will take care of pausing all your animations in current state and re-adding it when app comes from background - without resetting them.

Gist: https://gist.github.com/grzegorzkrukowski/a5ed8b38bec548f9620bb95665c06128

查看更多
登录 后发表回答