Animating the mask for a UIView

soleil picture soleil · Aug 23, 2013 · Viewed 8.9k times · Source

I can create a mask like this:

CALayer *mask = [CALayer layer];
    mask.contents = (id)[[UIImage imageNamed:@"mask.png"] CGImage];
    mask.frame = CGRectMake(0, 0, 10, 10);
    self.content.layer.mask = mask;

And this will correctly reveal the top left 10 pixels of my content (because mask.png is just a black image). However I want to animate the mask to reveal the rest of the content:

 [UIView animateWithDuration:3.0
                     animations:^{
                         mask.frame = self.content.bounds;
                     }
                     completion:^(BOOL finished){

                     }];

The problem is that there's no animation. The entire content gets displayed immediately. Why does this happen, and how can I animate the mask so that the content is revealed from the upper left?

Answer

WDUK picture WDUK · Aug 23, 2013

The frame is a derived property of various other properties, such as the position, bounds, anchorPoint, and any transform it has applied to it. It's not recommended to animate this property directly, especially when dealing with lower level CoreAnimation layers.

In this case, I would assume you want to animate the bounds. You can use the UIView animation method above, but when dealing with CALayers directly I prefer to use the CoreAnimation methods of animation.

CGRect oldBounds = mask.bounds;
CGRect newBounds = self.content.bounds;

CABasicAnimation* revealAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
revealAnimation.fromValue = [NSValue valueWithCGRect:oldBounds];
revealAnimation.toValue = [NSValue valueWithCGRect:newBounds];
revealAnimation.duration = 3.0;

// Update the bounds so the layer doesn't snap back when the animation completes.
mask.bounds = newBounds;

[mask addAnimation:revealAnimation forKey:@"revealAnimation"];