UIView animation stops when view disappears, doesn't resume when it re-appears
Asked Answered
R

6

17

Here's my config:

I have a storyboard with a ListViewController. ListViewController has a subview UIView called EmptyListView. EmptyListView is shown only when there are no items in the UITableView, otherwise it is hidden.

EmptyListView has a subview UIImageView called emptyListViewArrow which visually points towards the button to create a new entry in my UITableView. This arrow is animated infinitely in an up & down fashion.

I listen for EmptyListView to send a notification when it has finished laying out it's subviews. I do this because if not, animations that mutate constraints behave incorrectly.

- (void) layoutSubviews {
    [super layoutSubviews];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"EmptyViewDidLayoutSubviews" object:nil];
}

When ListViewController observes this notification, it begins the animation:

[UIView animateWithDuration:0.8f delay:0.0f options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse) animations:^{
    self.emptyListViewArrowVerticalSpace.constant = 15.0f;
    [emptyListView layoutIfNeeded];
} completion:nil];

If I put the app in the background or push another ViewController on the stack, once I come back to the app or the ListViewController, the animation is stopped/paused. I can't get it to start again.

I've tried:

  1. Listening for application will resign/become active. When resigning, I did [self.view.layer removeAllAnimations] and then tried to start the animation again using the code above.
  2. Removing the UIImageView being animated and adding it back to the parent view, and then tried to start the animation.

I'm regretting using constraints here, but would love any insight into what could be the problem.

Thank you!

Revis answered 5/9, 2013 at 3:41 Comment(2)
Are you using your methods in viewDidLoad or viewWillAppear?Secundas
viewDidAppear, but not for any particular reason.Revis
L
13

I'm not sure what you're doing with that notification. I've done lots of animations with constraints and never had to do that. I think the problem is that when you leave the view, the constraint's constant value will be 15 (I've verified that with logs in viewWillDisappear), so the animation to set it to 15 will do nothing. In a test app, I set the constant back to its starting value (0) in viewWillAppear, and it worked fine:

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    self.bottomCon.constant = 0;
    [self.view layoutIfNeeded];
}


-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [UIView animateWithDuration:1 delay:0.0f options:UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse  animations:^{
        self.bottomCon.constant = -40.0f;
        [self.view layoutIfNeeded];
    } completion:nil];
}
Lemley answered 5/9, 2013 at 4:21 Comment(0)
V
20

I was having the same issue. Resolved with the following.

yourCoreAnimation.isRemovedOnCompletion = false

Or you have group of animations

yourGroupAnimation.isRemovedOnCompletion = false
Valuation answered 1/10, 2018 at 4:43 Comment(2)
Do you know if it will prevent the view controller from deallocate?Polymer
It works! Thank you! If completion block of CATransaction is defined - it won't work. In me case I roved completion and add isRemovedOnCompletion - it fixed issue for me.Bouilli
L
13

I'm not sure what you're doing with that notification. I've done lots of animations with constraints and never had to do that. I think the problem is that when you leave the view, the constraint's constant value will be 15 (I've verified that with logs in viewWillDisappear), so the animation to set it to 15 will do nothing. In a test app, I set the constant back to its starting value (0) in viewWillAppear, and it worked fine:

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    self.bottomCon.constant = 0;
    [self.view layoutIfNeeded];
}


-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [UIView animateWithDuration:1 delay:0.0f options:UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse  animations:^{
        self.bottomCon.constant = -40.0f;
        [self.view layoutIfNeeded];
    } completion:nil];
}
Lemley answered 5/9, 2013 at 4:21 Comment(0)
S
4

This is the drawback. To overcome this you have to again call the animation method.

You can do it as follows :

Add Observer in your controller

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resumeAnimation:) name:UIApplicationDidBecomeActiveNotification object:nil];

This will get invoked once you resume the app.

Add this method :

- (void)resumeAnimation:(NSNotification *)iRecognizer {
    // Restart Animation
}

Hope this will help you.

Summertree answered 5/9, 2013 at 4:29 Comment(0)
P
4

I was having the same problem. I applied animation to a an imageView in viewDidLoad.

UIView.animate(withDuration: 0.5, delay: 0, options: [.repeat, .autoreverse, ], animations: {

    self.myImageView.transform = self.transform
    self.myImageView.alpha = 0.7

  }

But whenever i pushed some other VC and came back, animation was not working.

What worked for me

  1. I had to reset the animation in viewWillDisappear like this

    myImageView.transform = .identity

    myImageView.alpha = 1

  2. Write the above animation block ( UIView.animate()) in viewWillAppear ALSO.

Hope this helps

Peseta answered 18/2, 2019 at 5:51 Comment(0)
T
2

I was having the same issue as You. Resolved with the following:

animation.isRemovedOnCompletion = false ( default = true : allow removing animation when completed or view disappeared )

Note: If you have group animations

groupdAnimation.isRemovedOnCompletion = false
Thierry answered 23/8, 2022 at 18:57 Comment(0)
N
0

The same problem you will face when the app goes in background and again back to the foreground :

Swift :

NotificationCenter.default.addObserver(self, selector: #selector(enterInForeground), name: NSNotification.Name(rawValue: UIApplication.willEnterForegroundNotification.rawValue), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(enterInBackground), name: NSNotification.Name(rawValue: UIApplication.didEnterBackgroundNotification.rawValue), object: nil)

@objc func enterInForeground() {
     self.animateTheLaserLine()
}
@objc func enterInBackground() {
     // reset the position when app enters in background
     // I have added the animation on layout constrain you can take your 
     // component 
     self.laserTopConstrain.constant = 8
}
Nelia answered 29/6, 2018 at 18:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.