I have an app where I have a UIImageView
which displays main image and another UIImageView
being used as a mask which shows a circle which is transparent and outside its opaque, this circle can be moved using a UIPanGestureRecognizer
, I want to know a wayout to crop the image inside the circle into a new image. Here is the attached code and screen shot
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// create pan gesture
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(handlePan:)];
[self.view addGestureRecognizer:pan];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [[self makeCircleAtLocation:self.view.center radius:100.0] CGPath];
shapeLayer.strokeColor = [[UIColor clearColor] CGColor];
shapeLayer.fillColor = nil;
shapeLayer.lineWidth = 3.0;
// Add CAShapeLayer to our view
[self.view.layer addSublayer:shapeLayer];
// Save this shape layer in a class property for future reference,
// namely so we can remove it later if we tap elsewhere on the screen.
self.circleLayer = shapeLayer;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// Create a UIBezierPath which is a circle at a certain location of a certain radius.
// This also saves the circle's center and radius to class properties for future reference.
- (UIBezierPath *)makeCircleAtLocation:(CGPoint)location radius:(CGFloat)radius
{
self.circleCenter = location;
self.circleRadius = radius;
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:self.circleCenter
radius:self.circleRadius
startAngle:0.0
endAngle:M_PI * 2.0
clockwise:YES];
return path;
}
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
static CGPoint oldCenter;
if (gesture.state == UIGestureRecognizerStateBegan)
{
// If we're starting a pan, make sure we're inside the circle.
// So, calculate the distance between the circle's center and
// the gesture start location and we'll compare that to the
// radius of the circle.
CGPoint location = [gesture locationInView:gesture.view];
CGPoint translation = [gesture translationInView:gesture.view];
location.x -= translation.x;
location.y -= translation.y;
CGFloat x = location.x - self.circleCenter.x;
CGFloat y = location.y - self.circleCenter.y;
CGFloat distance = sqrtf(x*x + y*y);
// If we're outside the circle, cancel the gesture.
// If we're inside it, keep track of where the circle was.
oldCenter = self.circleCenter;
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
// Let's calculate the new center of the circle by adding the
// the translationInView to the old circle center.
CGPoint translation = [gesture translationInView:gesture.view];
CGPoint newCenter = CGPointMake(oldCenter.x + translation.x, oldCenter.y + translation.y);
// CGPoint newCenter = [gesture locationInView:self.view];
if (newCenter.x < 160) {
newCenter.x = 160;
}
else if (newCenter.x > self.view.frame.size.width - 160) {
newCenter.x = self.view.frame.size.width - 160;
}
if (newCenter.y < 242) {
newCenter.y = 242;
}
else if (newCenter.y > self.view.frame.size.height - imageMain.center.y) {
newCenter.y = self.view.frame.size.height - imageMain.center.y;
}
// Update the path for our CAShapeLayer
// self.circleLayer.path = [[self makeCircleAtLocation:newCenter radius:self.circleRadius] CGPath];
imageCircle.center = newCenter;
}
}
@end
And the result is
To save the masked image, in iOS 7 you'd use
drawViewHierarchyInRect
, and in earlier versions of iOS, you'd userenderInContext
. You might also want to crop the image withCGImageCreateWithImageInRect
. See myhandleTap
below for an example.But I note that you are apparently masking by overlaying an image. I might suggest using a
UIBezierPath
for the basis of both a layer mask for the image view, as well as theCAShapeLayer
you'll use to draw the circle (assuming you want a border as you draw the circle. If your mask is a regular shape (such as a circle), it's probably more flexible to make it aCAShapeLayer
with aUIBezierPath
(rather than an image), because that way you can not only move it around with a pan gesture, but also scale it, too, with a pinch gesture:Here is a sample implementation:
If you don't want to draw the border around the circle, then it's even easier, as you can pull anything related to
circleLayer
.If you're interested in Objective-C example, see previous revision of this answer.