1
votes

I have two animations which are overlapping, which, because of the way I've set my method up, causes the second one not to fire. I have a check like so in the beginning of the method:

- (void)animateHidden:(BOOL)hidden duration:(CGFloat)seconds delay:(CGFloat)delay options:(UIViewAnimationOptions)options disableUserInteraction:(BOOL)disableUserInteraction {
    if (self.hidden == hidden) {
        return;
    }

Then, further down, my animation block looks like so:

__weak UIView *weakSelf = self;
[UIView animateWithDuration:seconds delay:delay options:options animations:^{
    weakSelf.alpha = hidden ? 0 : 1;
} completion:^(BOOL finished) {
    // Return user interaction to previous state
    if (disableUserInteraction) {
        weakSelf.userInteractionEnabled = userInteractionEnabled;
    }
    weakSelf.hidden = hidden;
}];

Two animations are kicked off on the same view, one before a service call and one after. If the service call happens quick enough that the view is still animating, weakSelf.hidden = hidden; will never be called, and the second animation will exit out since the hidden value wasn't updated in time.

Is there anyway that I could force the completion block on the animation block to be called? I need to update my hidden property before making the check, but can't find a way to accomplish this.

Calling [self.layer removeAllAnimations] doesn't seem to work unfortunately.

3
Not sure what else you might be doing in the methods, but... Would it work for you if - instead of using your hidden property / variable, check the .alpha value? That would tell you if the animation is finished or not.DonMag
Completion block should be called anyway. What you can do? The simples/quickest way is to make flags and check is completion already happened.Axel
@DonMag The issue with that is that because when we call this method, the view may have originally been hidden with an alpha of 1, so checking for an alpha value that corresponds to a hidden value doesn't work for all views.Bill L

3 Answers

0
votes

You can use CABasicAnimation instead of UIView animation, that will solve the problem more accurately.

You can use it like:

CABasicAnimation* opacityZero= [CABasicAnimation animationWithKeyPath:@"opacity"];
[opacityZero setToValue:[NSNumber numberWithFloat:0.0]];
[opacityZero setDuration:duration];
[[self layer] addAnimation:opacityZero forKey:@"opacityZero"];

And when your service call ends, you can call [self.layer removeAllAnimations];

Similarly, you can make the opacity one and tweak the above method as you like.

You can find more info here.

0
votes

A __block prefixed to hidden attribute declaration should help.
Something like,
@property (nonatomic) __block BOOL hidden;

0
votes

If you are targeting iOS 10+ take a look at UIViewPropertyAnimator https://developer.apple.com/reference/uikit/uiviewpropertyanimator?language=objc

Combined with the UIViewAnimating and UIViewImplicitlyAnimating protocols, this allows modification / interruption / pause / resume / stop / etc of the animations.

Basic example (buttons and view set in IB):

- (IBAction)startTapped:(id)sender {

    _myAnimator = [UIViewPropertyAnimator
                   runningPropertyAnimatorWithDuration:3.0
                   delay:0.0
                   options:UIViewAnimationOptionCurveLinear
                   animations:^{
                       _theRedBox.alpha = _theRedBox.alpha > 0 ? 0 : 1;
                   } completion:^(UIViewAnimatingPosition finalPosition) {
                       // do stuff
                   }];

}

- (IBAction)stopTapped:(id)sender {

    [_myAnimator stopAnimation:NO];
    [_myAnimator finishAnimationAtPosition:UIViewAnimatingPositionEnd];

}