5
\$\begingroup\$

I'm new to Core Animations, I want to fade out two different layers one after another.I write them in a CATransaction block. I wonder if there is better approach to make such animations.

[CATransaction begin];

    CABasicAnimation *fadeOutLoadingBar = [CABasicAnimation animationWithKeyPath:@"opacity"];
    fadeOutLoadingBar.fromValue = [NSNumber numberWithFloat:0.3];
    fadeOutLoadingBar.toValue = [NSNumber numberWithFloat:0.];
    fadeOutLoadingBar.fillMode = kCAFillModeForwards;
    fadeOutLoadingBar.beginTime = [_loadingLayer convertTime:CACurrentMediaTime() fromLayer:nil];
    fadeOutLoadingBar.duration = .8;
    fadeOutLoadingBar.removedOnCompletion = NO;
    [_loadingLayer addAnimation:fadeOutLoadingBar forKey:nil];

    [self setupCheckMarkLayer]; //initialize _checkMarkLayer 
    CABasicAnimation *fadeOutCheckMark = [CABasicAnimation animationWithKeyPath:@"opacity"];
    fadeOutCheckMark.fromValue = [NSNumber numberWithFloat:0.3];
    fadeOutCheckMark.toValue = [NSNumber numberWithFloat:0.];
    fadeOutCheckMark.fillMode = kCAFillModeForwards;
    fadeOutCheckMark.beginTime = [_checkMarkLayer convertTime:CACurrentMediaTime() fromLayer:nil] + 0.5;
    fadeOutCheckMark.duration = 1.0;
    fadeOutCheckMark.removedOnCompletion = NO;
    _checkMarkLayer.opacity = 0.;
    [_checkMarkLayer addAnimation:fadeOutCheckMark forKey:nil];

    [CATransaction setCompletionBlock:^{
        self.userInteractionEnabled = YES;
        [self.delegate finishedLoading];
        [self setupLoadingLayer];
    }];

    [CATransaction commit];
\$\endgroup\$
2
  • \$\begingroup\$ Does [NSNumber numberWithFloat:0.] actually work? \$\endgroup\$
    – nhgrif
    Commented Nov 13, 2014 at 22:34
  • \$\begingroup\$ yes,it works :) try it \$\endgroup\$
    – sftsz
    Commented Nov 14, 2014 at 9:15

2 Answers 2

3
\$\begingroup\$

We have some magic numbers. Also, why don't we use modern syntax for our NSNumber objects?

NSNumber *opacityFromValue = @0.3f;
NSNumber *opacityToValue = @0.0f;

CGFloat fadeLoadBarDuration = 0.8;
CGFloat fadeCheckmarkDuration = 1.0;

CGFloat timeCheckmarkOffset = 0.5;

And now use these variables in place of the magic numbers.

As an added bonus, we'll get slightly better performance by not having to instantiate the opacity values twice each. NSNumber is an immutable object, so it doesn't matter that we pass the same one both times.

\$\endgroup\$
2
  • \$\begingroup\$ okay I changed those values thank you, do you also have a suggestion for animation block? \$\endgroup\$
    – sftsz
    Commented Nov 14, 2014 at 9:19
  • \$\begingroup\$ I'm not particularly familiar with CoreAnimation. I'll only comment on what I actually know. \$\endgroup\$
    – nhgrif
    Commented Nov 15, 2014 at 12:53
1
\$\begingroup\$

As an aside, I do want to comment on this line:

[self.delegate finishedLoading];

Consider this, there's nothing that prevents your delegate from delegating multiple objects. Moreover, finishedLoading might be a fairly common method name multiple protocols might like to define.

So, let's presume our class that has this delegate is called MyAnimatingImage, and the protocol is called MyAnimatingImageDelegate. Rather than looking like this:

@protocol MyAnimatingImageDelegate

@required - (void)finishedLoading;

@end

We will be better off with a protocol that looks like this:

@protocol MyAnimatingImageDelegate

@required - (void)animatingImageDidFinishLoading:(MyAnimatingImage *)animatingImage;

@end

And when we call the method, we do so as such:

[self.delegate animatingImageDidFinishLoading:self];

We pass the delegate a reference to self. Yes, in most cases the delegate will already have a reference to us. And in a lot of cases, the delegate may not be using multiple objects of this type, so it'll always know what finishedLoading is talking about... but consider this case:

- (void)viewDidLoad {
    self.object1 = [MyAnimatingImage animatingImage];
    self.object2 = [MyAnimatingImage animatingImage];

    [object1 beginLoading];
    [object2 beginLoading];
}

Now, when object1 finishes loading and calls finishedLoading without passing a reference to itself, how does our delegate know whether object1 or object2 is finished loading? It can't. It might assume that object1 is always finished loading first, but that's not always going to be the case, particularly if there's anything asynchronous about the loading.

\$\endgroup\$
2
  • \$\begingroup\$ you are right. I'm going to change it. thank you for your help. \$\endgroup\$
    – sftsz
    Commented Nov 15, 2014 at 14:05
  • \$\begingroup\$ I also created a __block variable for self. __block id< MyAnimatingImage > delegate= self.delegate; \$\endgroup\$
    – sftsz
    Commented Nov 15, 2014 at 15:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.