1

I am setting a mask with all rounded corners path to a view like this:

CGFloat cornerRadius = CGRectGetHeight(self.myView.bounds) / 2;
CGSize cornerRadiusSize = CGSizeMake(cornerRadius, cornerRadius);
CGRect bounds = self.myView.bounds;

CAShapeLayer *maskLayer = [CAShapeLayer layer];

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:cornerRadiusSize];

maskLayer.path = maskPath.CGPath;

self.myView.layer.mask = maskLayer;

that looks like this:

screenshot 1

I am trying to animate it to a path with top left and top right rounded corners like this:

UIBezierPath *newMaskPath = [UIBezierPath bezierPathWithRoundedRect:bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:cornerRadiusSize];

CABasicAnimation *maskPathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];

maskPathAnimation.duration = 0.3;

maskPathAnimation.fromValue = (id)maskLayer.path;
maskPathAnimation.toValue = (id)newMaskPath.CGPath;

[maskLayer addAnimation:maskPathAnimation forKey:nil];

maskLayer.path = newMaskPath.CGPath;

that looks like this:

screenshot 2

But the animation is mangled, it looks like it animates some corners to other corners:

screenshot 3

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
  • What happens if you remove the last line? `maskLayer.path = newMaskPath.CGPath;` – chedabob Jul 03 '17 at 09:58
  • It animates the same as before but at the end of the animation, the mask snaps back to the original path. – Iulian Onofrei Jul 03 '17 at 10:23
  • You need to set fill mode and removeoncompletion: https://stackoverflow.com/questions/23806724/cabasicanimation-back-to-its-original-position-after-it-finish-its-1-cycle – chedabob Jul 03 '17 at 10:24
  • That is a bad practice and should be avoided, and it's a solution to a problem caused by removing that line. Not removing it "fixes" this jumping problem. The original problem is about the animation between two different paths. – Iulian Onofrei Jul 03 '17 at 10:27

1 Answers1

1

I solved it by creating the mask paths by hand using this method:

- (UIBezierPath *)getMaskPathForView:(UIView *)view withCorners:(UIRectCorner)corners {

    CGSize size = view.bounds.size;
    CGFloat cornerRadius = size.height / 2;

    UIBezierPath *maskPath = [UIBezierPath bezierPath];

    // top left
    if (corners & UIRectCornerTopLeft) {
        [maskPath moveToPoint:CGPointMake(0, cornerRadius)];
        [maskPath addQuadCurveToPoint:CGPointMake(cornerRadius, 0) controlPoint:CGPointZero];
    } else {
        CGPoint topLeftCornerPoint = CGPointZero;

        [maskPath moveToPoint:topLeftCornerPoint];
        [maskPath addQuadCurveToPoint:topLeftCornerPoint controlPoint:topLeftCornerPoint];
    }

    // top right
    if (corners & UIRectCornerTopRight) {
        [maskPath addLineToPoint:CGPointMake(size.width - cornerRadius, 0)];
        [maskPath addQuadCurveToPoint:CGPointMake(size.width, cornerRadius) controlPoint:CGPointMake(size.width, 0)];
    } else {
        CGPoint topRightCornerPoint = CGPointMake(size.width, 0);

        [maskPath addLineToPoint:topRightCornerPoint];
        [maskPath addQuadCurveToPoint:topRightCornerPoint controlPoint:topRightCornerPoint];
    }

    // bottom right
    if (corners & UIRectCornerBottomRight) {
        [maskPath addLineToPoint:CGPointMake(size.width, size.height - cornerRadius)];
        [maskPath addQuadCurveToPoint:CGPointMake(size.width - cornerRadius, size.height) controlPoint:CGPointMake(size.width, size.height)];
    } else {
        CGPoint bottomRightCornerPoint = CGPointMake(size.width, size.height);

        [maskPath addLineToPoint:bottomRightCornerPoint];
        [maskPath addQuadCurveToPoint:bottomRightCornerPoint controlPoint:bottomRightCornerPoint];
    }

    // bottom left
    if (corners & UIRectCornerBottomLeft) {
        [maskPath addLineToPoint:CGPointMake(cornerRadius, size.height)];
        [maskPath addQuadCurveToPoint:CGPointMake(0, size.height - cornerRadius) controlPoint:CGPointMake(0, size.height)];
    } else {
        CGPoint bottomLeftCornerPoint = CGPointMake(0, size.height);

        [maskPath addLineToPoint:bottomLeftCornerPoint];
        [maskPath addQuadCurveToPoint:bottomLeftCornerPoint controlPoint:bottomLeftCornerPoint];
    }

    [maskPath closePath];

    return maskPath;
}

like this:

CAShapeLayer *maskLayer = [CAShapeLayer layer];

UIBezierPath *maskPath = [self getMaskPathForView:self.myView withCorners:UIRectCornerAllCorners];

maskLayer.path = maskPath.CGPath;

self.myView.layer.mask = maskLayer;

and:

UIBezierPath *newMaskPath = [self getMaskPathForView:self.myView withCorners:UIRectCornerTopLeft | UIRectCornerTopRight];

CABasicAnimation *maskPathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];

maskPathAnimation.duration = 0.3;

maskPathAnimation.fromValue = (id)maskLayer.path;
maskPathAnimation.toValue = (id)newMaskPath.CGPath;

[maskLayer addAnimation:maskPathAnimation forKey:nil];

maskLayer.path = newMaskPath.CGPath;
Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113