简体   繁体   中英

How to animate a CALayer attached to UIImageView?

I am using this code proposed by Bartosz to add a mask to an UIImageView. It works fine.

#import <QuartzCore/QuartzCore.h>

CALayer *mask = [CALayer layer];
mask.contents = (id)[[UIImage imageNamed:@"mask.png"] CGImage];
mask.frame = CGRectMake(0, 0, 320.0, 100.0);
yourImageView.layer.mask = mask;
yourImageView.layer.masksToBounds = YES;

In addition, I want to animate the mask, eg sliding the mask to the right, so that at the end of the animation, the mask is not applied to the UIImageView any more.

In my specific case, the mask uses a fully transparent image, so the UIImageView is not visible at the initial state (which works fine), but is expected to be so at the end of the animation. However, the idea may be reused to any other use case were masks need to be animated.

The idea is to manipulate the x-origin portion of the frame of the mask. So, I came up with this code:

[UIView animateWithDuration: 0.2
                      delay: 0
                    options: UIViewAnimationCurveEaseInOut
                 animations:^{
                         CGRect maskFrame = yourImageView.layer.mask.frame;
                         maskFrame.origin.x = 320.0;
                         yourImageView.layer.mask.frame = maskFrame;
                     }
                 completion:^(BOOL finished){}];

Unfortunately, the mask is applied to the whole UIImageView at any time, it's not sliding to the right.

UPDATE 1:

This is the code I am actually using the set up the view and mask: It's a UITableViewCell.

APPCell.m (APPCell.h "extends" UITableViewCell)

#import "APPCell.h"
#import <QuartzCore/QuartzCore.h>

@interface APPCell()
@property (strong, nonatomic) UIImageView *menu;
@property (strong, nonatomic) CALayer *menuMask;
...
@end

@implementation APPCell
...

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self.menu = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320.0, 88.0)];
[self.menu setBackgroundColor:[UIColor clearColor]];
[self.menu setImage:[UIImage imageNamed:@"cell_back"]];
[self addSubview:self.menu];

self.menuMask = [CALayer layer];
self.menuMask.contents = (id)[[UIImage imageNamed:@"cell_mask"] CGImage];
self.menuMask.frame = CGRectMake(0, 0, 320.0, 88.0);
self.menu.layer.mask = self.menuMask;
self.menu.layer.masksToBounds = YES;
}
...

Instead of animating with the help of UIKit, I am now using implicit animation of CoreAnimation to move the mask layer:

APPCell.m

...
- (void)swipeLeft
{
self.menuMask.position = CGPointMake(-320.0, 0.0);
}
...

I can confirm that swipeLeft is called. I expect the mask "to be gone" and to see the [UIImage imageNamed:@"cell_back"]] , which I do when I uncomment self.menu.layer.mask = self.menuMask .

Solution:

Instead of setting the content on the CALayer, I set the background color to white. This is the code I am using:

self.menuSubMenuMask = [CALayer layer];
self.menuSubMenuMask.backgroundColor = [[UIColor whiteColor] CGColor];
self.menuSubMenuMask.frame = CGRectMake(320.0, 0.0, 320.0, 88.0);
self.tableCellSubMenu.layer.mask = self.menuSubMenuMask;
self.tableCellSubMenu.layer.masksToBounds = YES;

In order to show the UIImageView the CALayer is applied to, the CALayer must NOT be "above" the UIImageView.

Animation with UIKit of UIViews is much more limited than using Core Animation directly. In particular what you are trying to animate is not one of animatable properties of a UIView. In addition as clarified in the View Programming Guide for iOS :

Note: If your view hosts custom layer objects—that is, layer objects without an associated view—you must use Core Animation to animate any changes to them.

This is the case in your example. You have added a CALayer to your view and UIKit will not be able to animate the result for you. On the other hand you can use Core Animation directly to animate the motion of your mask layer. You should be able to do this easily using implicit animation as described in the Core Animation Programming Guide . Please note that from the list of CALayer Animatable Properties that frame is not animatable. Instead you should use position .

You can achieve something you want by using CATransition , although this might not be the solution you want:

1) At first, set mask for your layer just as you did

2) When you want to remove mask and reveal your image, use the following code:

CATransition* transition = [CATransition animation];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
transition.duration = 1.0;
[mask addAnimation:transition forKey:kCATransition];
imageView.layer.mask.contents = [UIImage imageNamed:@"black.png"].CGImage;

The main trick here - we created transition animation for our mask layer, so this animation will be applied when you change any (i'm not sure about any) property of mask layer. Now we set mask's content to completely black image to remove masking at all - now we've got smooth pushing animation where our masked image is going to the left and unmasked image is getting into its place

The easiest way is to use CoreAnimation itself:

CGPoint fromPoint = mask.position;
CGPoint toPoint = CGPointMake(fromPoint.x*3.0, fromPoint.y);

mask.position = toPoint; // CoreAnimation animations do *not* persist

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:fromPoint];
animation.toValue = [NSValue valueWithCGPoint:toPoint];
animation.duration = 4.0f;

[mask addAnimation:animation forKey:@"position"];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM