Assuming this is eight pill shapes that will reveal a single image underneath, you
- create a
UIBezierPath
for the eight pill shapes - create a
CAShapeLayer
layer that uses thatUIBezierPath
(or more accurately, itsCGPath
) - apply this
CAShapeLayer
as themask
for thelayer
for yourUIImageView
If you want to animate this over time, just replace the mask
(e.g. in response to a accelerometer event, a CADisplayLink
or NSTimer
, or a UIPanGesture
), or just update the path
of the CAShapeLayer
you have applied as the mask to the UIImageView
's layer.
Thus, you might have something like:
- (void)addMaskToView:(UIView *)view withAdjustment:(CGFloat)adjustment
{
CAShapeLayer *mask = [CAShapeLayer layer];
mask.path = [[self pillsPathWithAdjustment:adjustment forView:view] CGPath];
self.imageView.layer.mask = mask;
}
- (UIBezierPath *)pillsPathWithAdjustment:(CGFloat)adjustment forView:(UIView *)view
{
UIBezierPath *path = [UIBezierPath bezierPath];
for (NSInteger i = 0; i < kNumberOfPills; i++)
[self addPillPathNumber:i forView:view toPath:path adjustment:adjustment];
return path;
}
- (void)addPillPathNumber:(NSInteger)index forView:(UIView *)view toPath:(UIBezierPath *)path adjustment:(CGFloat)adjustment
{
CGFloat const pillSpacing = 5.0f;
CGFloat const pillWidth = view.bounds.size.width / kNumberOfPills - pillSpacing;
CGFloat const pillHeight = view.bounds.size.height * 0.4;
CGFloat const cornerRounding = 10.0f;
CGPoint point = CGPointMake(index * (pillWidth + pillSpacing) + pillSpacing / 2.0, sinf((float) index * M_PI / kNumberOfPills + adjustment * M_PI * 2.0) * 100.0);
// top
point.x += cornerRounding;
[path moveToPoint:point];
point.x += pillWidth - cornerRounding * 2.0;
[path addLineToPoint:point];
// top right corner
[path addArcWithCenter:CGPointMake(point.x, point.y + cornerRounding) radius:cornerRounding startAngle:3 * M_PI_2 endAngle:2.0 * M_PI clockwise:YES];
point.x += cornerRounding;
point.y += cornerRounding;
// right
point.y += pillHeight - cornerRounding * 2.0;
[path addLineToPoint:point];
// lower right corner
[path addArcWithCenter:CGPointMake(point.x - cornerRounding, point.y) radius:cornerRounding startAngle:0 endAngle:M_PI_2 clockwise:YES];
// bottom
point.y += cornerRounding;
point.x -= cornerRounding;
point.x -= pillWidth - cornerRounding * 2.0;
[path addLineToPoint:point];
// lower left corner
[path addArcWithCenter:CGPointMake(point.x, point.y - cornerRounding) radius:cornerRounding startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
// left
point.y -= cornerRounding;
point.x -= cornerRounding;
point.y -= pillHeight - cornerRounding * 2.0;
[path addLineToPoint:point];
// upper left corner
[path addArcWithCenter:CGPointMake(point.x + cornerRounding, point.y) radius:cornerRounding startAngle:M_PI endAngle:3.0 * M_PI_2 clockwise:YES];
[path closePath];
}
By way of illustration, this is how I'd animate this using a CADisplayLink
:
- (void)startDisplayLink
{
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
self.startTime = CACurrentMediaTime();
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink
{
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
CGFloat seconds;
CGFloat fractionsOfSecond = modff(CACurrentMediaTime() - self.startTime, &seconds);
[self addMaskToView:self.imageView withAdjustment:fractionsOfSecond];
}