11

I would like to add a circular mask to UIImageVIew. Here is the function that am using to add the mask..

- (void) addMaskToBounds:(CGRect) maskBounds
{
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];

    CGPathRef maskPath = CGPathCreateWithEllipseInRect(maskBounds, NULL);
    maskLayer.bounds = maskBounds;
    [maskLayer setPath:maskPath];
    [maskLayer setFillColor:[[UIColor blackColor] CGColor]];
    maskLayer.position = CGPointMake(maskBounds.size.width/2, maskBounds.size.height/2);

    [self.imageView.layer setMask:maskLayer];
}

Am able to add the mask but the problem occurs since am changing the image frame and center along with it. The image is enlarged and made small based on the user actions. Hence I would have to add the mask twice when small and when enlarged. So Since am animating the frame change, while animating the image become distorted, or displaced. How do I overcome this ?

I guess its best explained with an example project. Here I have created a Demo project https://github.com/akshaynhegde/MaskImage on GitHub, just run it to see my problem and let me know how resolve the problem.

akshaynhegde
  • 1,938
  • 3
  • 17
  • 36

2 Answers2

23

Hope this will help

    CAShapeLayer *circle = [CAShapeLayer layer];
    // Make a circular shape
    UIBezierPath *circularPath=[UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, _imgView.frame.size.width, _imgView.frame.size.height) cornerRadius:MAX(_imgView.frame.size.width, _imgView.frame.size.height)];

    circle.path = circularPath.CGPath;

    // Configure the apperence of the circle
    circle.fillColor = [UIColor blackColor].CGColor;
    circle.strokeColor = [UIColor blackColor].CGColor;
    circle.lineWidth = 0;

    imgViewToClipCircular.layer.mask=circle;
Mrug
  • 4,963
  • 2
  • 31
  • 53
  • This doesn't solve the initial question of how to animate frame changes to `UIImageView` when using a mask at all... – Sam Jul 29 '16 at 11:16
  • I found this code very handy because I needed an off-center circular mask for a view I was creating. First I used layer.cornerRadius to make the view circular in Interface Builder. Then I used this code with a negative Y value in the Bezier path to create a mask for it. The resulting shape looks a lot like an American football, which is what I needed. – Carl Smith Sep 06 '16 at 21:46
4

Ok, so since the center in my case changes, using the mask is not ging to help. So the easy and clean way is to have a custom UiImageView and clip the draw path.

So for that, subclass UIView and draw the image inside it and clip the path. Here is the overriden the drawRect function,

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextAddEllipseInRect(context, self.bounds);
    CGContextClip(context);
    //The subclass of UIView has a UIImageview as property
    [_image drawInRect:rect];    
}

FYI, you can not subclass UIImageView itself and override drawRect , coz it won't call the drawRect,

The UIImageView class is optimized to draw its images to the display. UIImageView will not call drawRect: a subclass. If your subclass needs custom drawing code, it is recommended you use UIView as the base class.

akshaynhegde
  • 1,938
  • 3
  • 17
  • 36
  • The custom UIImageView solution was what I was previously using, so this solution worked was appropriate for me. It also looks closer to what iOS 7 does – CVertex Dec 07 '13 at 10:52
  • 1
    You can change centre of mask layer also. – Mrug Aug 22 '14 at 14:11
  • Note that your comment is not quite correct -- this subclass of UIView must have a *UIImage* as a property, not a *UIImageView* – RobP May 18 '17 at 15:50