Rob
Rob

Reputation: 629

Masking a custom shape with UIImageView

I want to programatically crop a shape over my UIImageView. I know about creating a path with QuartzCore, but I don't understand context. Give me an example by subclassing UIImageView.

So how can I make an image go from this:

Original Album Cover

To this:

Cropped Album Cover

I also need the mask to be transparent

Upvotes: 3

Views: 1274

Answers (1)

Rob
Rob

Reputation: 438257

The easiest approach is to

  • create a UIBezierPath for the hexagon;
  • create a CAShapeLayer from that path; and
  • add that CAShapeLayer as a mask to the image view's layer.

Thus, it might look like:

CAShapeLayer *mask = [CAShapeLayer layer];
mask.path          = [[self polygonPathWithRect:self.imageView.bounds lineWidth:0.0 sides:6] CGPath];
mask.strokeColor   = [UIColor clearColor].CGColor;
mask.fillColor     = [UIColor whiteColor].CGColor;
self.imageView.layer.mask = mask;

where

/** Create UIBezierPath for regular polygon inside a CGRect
 *
 * @param square        The CGRect of the square in which the path should be created.
 * @param lineWidth     The width of the stroke around the polygon. The polygon will be inset such that the stroke stays within the above square.
 * @param sides         How many sides to the polygon (e.g. 6=hexagon; 8=octagon, etc.).
 *
 * @return              UIBezierPath of the resulting polygon path.
 */

- (UIBezierPath *)polygonPathWithRect:(CGRect)square
                            lineWidth:(CGFloat)lineWidth
                                sides:(NSInteger)sides
{
    UIBezierPath *path  = [UIBezierPath bezierPath];

    CGFloat theta       = 2.0 * M_PI / sides;                           // how much to turn at every corner
    CGFloat squareWidth = MIN(square.size.width, square.size.height);   // width of the square

    // calculate the length of the sides of the polygon

    CGFloat length      = squareWidth - lineWidth;
    if (sides % 4 != 0) {                                               // if not dealing with polygon which will be square with all sides ...
        length = length * cosf(theta / 2.0);                            // ... offset it inside a circle inside the square
    }
    CGFloat sideLength = length * tanf(theta / 2.0);

    // start drawing at `point` in lower right corner

    CGPoint point = CGPointMake(squareWidth / 2.0 + sideLength / 2.0, squareWidth - (squareWidth - length) / 2.0);
    CGFloat angle = M_PI;
    [path moveToPoint:point];

    // draw the sides and rounded corners of the polygon

    for (NSInteger side = 0; side < sides; side++) {
        point = CGPointMake(point.x + sideLength * cosf(angle), point.y + sideLength * sinf(angle));
        [path addLineToPoint:point];
        angle += theta;
    }

    [path closePath];

    return path;
}

I posted another answer that illustrates the idea with rounded corners, too.

If you want to implement the addition of this mask as part of a UIImageView subclass, I'll leave that to you. But hopefully this illustrates the basic idea.

Upvotes: 4

Related Questions