Animation of masked UIView, CALayer

2019-04-07 04:25发布

问题:

I am trying to achieve the following: The user taps on a view, a circular view pops up to the left of it with some image content in it. The view should animate in starting from the point of touch to the final frame which is outside the touched view and to the left. During the animation it should be a circle, growing into the right position and size

All works well with the code below, only that during the animation, circular boundary is only on the left. It's like the CALayer shape is sliding into its final frame.

It looks sort of like this.

Once the animation completes, I have the full circle as expected.

CGFloat w = 300;
CGRect frame = CGRectMake(myX, myY, w, w);
CGPoint p = [touch locationInView:self.imageView];
CGRect initialFrame = CGRectMake(p.x, p.y, 0,0);
UIImageView *circle = [[UIImageView alloc] initWithFrame:frame];
circle.image = [UIImage imageNamed:@"china"];
circle.contentMode = UIViewContentModeScaleAspectFill;
circle.backgroundColor = [UIColor clearColor];
circle.layer.borderWidth = 1;
circle.layer.borderColor = [UIColor grayColor].CGColor;
circle.layer.masksToBounds = YES;

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = CGRectMake(0, 0, w, w);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, nil, maskRect);
maskLayer.path = path;
CGPathRelease(path);
circle.layer.mask = maskLayer;

circle.frame = initialFrame;
[self.imageView addSubview:circle];
[UIView animateWithDuration:1.0 animations:^{
    circle.frame = frame;
}];

I have tried just working with the cornerRadius of the CALayer, but that does not lead to satisfactory results either, as the radius would have to change with the frame size as well.

回答1:

You are animating the frame but not the mask. The circular mask remains constant in size and your image frame is animated from the top left to the finale full size. That is why you get the circular clipping of the top left part of the image, until it gets to the finale frame size.

This is what basically happens in your animation:

You can try to animate a transform (which will transform the finale clipped image exactly as you want it to look) instead of animating the frame.

Something like:

// Don't animate the frame. Give it the finale value before animation.
circle.frame = frame;

// Animate a transform instead. For example, a scaling transform.
circle.transform = CGAffineTransformMakeScale(0, 0);
[UIView animateWithDuration:1.0 animations:^{
    circle.transform = CGAffineTransformMakeScale(1, 1);
}];


回答2:

Did you try playing with the circle view's autoresizingMask, allowing it to have flexible left/right margins?

This may play a role in the animation and explain why your view appears to "slide". (At least it's worth a try)