如何在iOS中的UIImageView的圈内裁剪图像

我有一个应用程序,我有一个UIImageView显示主图像和另一个UIImageView被用作一个面具,显示一个圆形,透明的,不透明的,这个圆形可以移动使用UIPanGestureRecognizer ,我想知道一个UIPanGestureRecognizer圈内的图像变成新的图像。 这里是附加的代码和屏幕截图

 - (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 

结果是

在这里输入图像说明

为了保存被蒙版的图片,在iOS 7中使用drawViewHierarchyInRect ,在早期的iOS版本中,使用renderInContext 。 你可能也想用CGImageCreateWithImageInRect裁剪图像。 有关示例,请参阅下面的我的didTouchUpInsideSaveButton

但我注意到,你显然是掩盖了一个图像。 我可能会build议使用UIBezierPath作为图像视图的图层蒙版以及用来绘制圆的CAShapeLayer的基础(假设在绘制圆时需要边框,如果蒙版是常规的(例如一个圆圈),使用UIBezierPath (而不是图片)作为CAShapeLayer可能更为灵活,因为这样你不仅可以用平移手势移动它,还可以用捏手势:

在这里输入图像说明

这是一个示例实现:

 // ViewController.m #import "ViewController.h" @interface ViewController () <UIGestureRecognizerDelegate> @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (nonatomic) CGFloat circleRadius; @property (nonatomic) CGPoint circleCenter; @property (nonatomic, weak) CAShapeLayer *maskLayer; @property (nonatomic, weak) CAShapeLayer *circleLayer; @property (nonatomic, weak) UIPinchGestureRecognizer *pinch; @property (nonatomic, weak) UIPanGestureRecognizer *pan; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // create layer mask for the image CAShapeLayer *maskLayer = [CAShapeLayer layer]; self.imageView.layer.mask = maskLayer; self.maskLayer = maskLayer; // create shape layer for circle we'll draw on top of image (the boundary of the circle) CAShapeLayer *circleLayer = [CAShapeLayer layer]; circleLayer.lineWidth = 3.0; circleLayer.fillColor = [[UIColor clearColor] CGColor]; circleLayer.strokeColor = [[UIColor blackColor] CGColor]; [self.imageView.layer addSublayer:circleLayer]; self.circleLayer = circleLayer; // create circle path [self updateCirclePathAtLocation:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0) radius:self.view.bounds.size.width * 0.30]; // create pan gesture UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; pan.delegate = self; [self.imageView addGestureRecognizer:pan]; self.imageView.userInteractionEnabled = YES; self.pan = pan; // create pan gesture UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)]; pinch.delegate = self; [self.view addGestureRecognizer:pinch]; self.pinch = pinch; } - (void)updateCirclePathAtLocation:(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]; self.maskLayer.path = [path CGPath]; self.circleLayer.path = [path CGPath]; } - (IBAction)didTouchUpInsideSaveButton:(id)sender { NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; NSString *path = [documentsPath stringByAppendingPathComponent:@"image.png"]; CGFloat scale = [[self.imageView.window screen] scale]; CGFloat radius = self.circleRadius * scale; CGPoint center = CGPointMake(self.circleCenter.x * scale, self.circleCenter.y * scale); CGRect frame = CGRectMake(center.x - radius, center.y - radius, radius * 2.0, radius * 2.0); // temporarily remove the circleLayer CALayer *circleLayer = self.circleLayer; [self.circleLayer removeFromSuperlayer]; // render the clipped image UIGraphicsBeginImageContextWithOptions(self.imageView.frame.size, NO, 0.0); CGContextRef context = UIGraphicsGetCurrentContext(); if ([self.imageView respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) { // if iOS 7, just draw it [self.imageView drawViewHierarchyInRect:self.imageView.bounds afterScreenUpdates:YES]; } else { // if pre iOS 7, manually clip it CGContextAddArc(context, self.circleCenter.x, self.circleCenter.y, self.circleRadius, 0, M_PI * 2.0, YES); CGContextClip(context); [self.imageView.layer renderInContext:context]; } // capture the image and close the context UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // add the circleLayer back [self.imageView.layer addSublayer:circleLayer]; // crop the image CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], frame); UIImage *croppedImage = [UIImage imageWithCGImage:imageRef]; // save the image NSData *data = UIImagePNGRepresentation(croppedImage); [data writeToFile:path atomically:YES]; // tell the user we're done [[[UIAlertView alloc] initWithTitle:nil message:@"Saved" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil] show]; } #pragma mark - Gesture recognizers - (void)handlePan:(UIPanGestureRecognizer *)gesture { static CGPoint oldCenter; CGPoint tranlation = [gesture translationInView:gesture.view]; if (gesture.state == UIGestureRecognizerStateBegan) { oldCenter = self.circleCenter; } CGPoint newCenter = CGPointMake(oldCenter.x + tranlation.x, oldCenter.y + tranlation.y); [self updateCirclePathAtLocation:newCenter radius:self.circleRadius]; } - (void)handlePinch:(UIPinchGestureRecognizer *)gesture { static CGFloat oldRadius; CGFloat scale = [gesture scale]; if (gesture.state == UIGestureRecognizerStateBegan) { oldRadius = self.circleRadius; } CGFloat newRadius = oldRadius * scale; [self updateCirclePathAtLocation:self.circleCenter radius:newRadius]; } #pragma mark - UIGestureRecognizerDelegate - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { if ((gestureRecognizer == self.pan && otherGestureRecognizer == self.pinch) || (gestureRecognizer == self.pinch && otherGestureRecognizer == self.pan)) { return YES; } return NO; } @end 

如果您不想在圆圈周围绘制边框, circleLayer容易,因为您可以拉动与circleLayer相关的任何circleLayer

重复使用相同的代码与Square一起玩,可能对其他人有帮助。

 #import "ViewController.h" @interface ViewController () <UIGestureRecognizerDelegate> @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (nonatomic) CGFloat circleRadius; @property (nonatomic) CGPoint circleCenter; @property (nonatomic) CGRect frame; @property (nonatomic, weak) CAShapeLayer *maskLayer; @property (nonatomic, weak) CAShapeLayer *circleLayer; @property (nonatomic, weak) UIPinchGestureRecognizer *pinch; @property (nonatomic, weak) UIPanGestureRecognizer *pan; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // create layer mask for the image CAShapeLayer *maskLayer = [CAShapeLayer layer]; self.imageView.layer.mask = maskLayer; self.maskLayer = maskLayer; // create shape layer for circle we'll draw on top of image (the boundary of the circle) CAShapeLayer *circleLayer = [CAShapeLayer layer]; circleLayer.lineWidth = 3.0; circleLayer.fillColor = [[UIColor clearColor] CGColor]; circleLayer.strokeColor = [[UIColor blackColor] CGColor]; [self.imageView.layer addSublayer:circleLayer]; self.circleLayer = circleLayer; // create circle path [self updateCirclePathAtLocation:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0) radius:self.view.bounds.size.width * 0.30]; // create pan gesture UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; pan.delegate = self; [self.imageView addGestureRecognizer:pan]; self.imageView.userInteractionEnabled = YES; self.pan = pan; // create pan gesture UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)]; pinch.delegate = self; [self.view addGestureRecognizer:pinch]; self.pinch = pinch; } - (void)updateCirclePathAtLocation:(CGPoint)location radius:(CGFloat)radius { self.circleCenter = location; self.circleRadius = radius; self.frame =CGRectMake(self.circleCenter.x/2, self.circleCenter.y/2, self.circleRadius, self.circleRadius); UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.frame cornerRadius:0]; // [path addArcWithCenter:self.circleCenter // radius:self.circleRadius // startAngle:0.0 // endAngle:M_PI * 2.0 // clockwise:YES]; self.maskLayer.path = [path CGPath]; self.circleLayer.path = [path CGPath]; } - (IBAction)didTouchUpInsideSaveButton:(id)sender { NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; NSString *path = [documentsPath stringByAppendingPathComponent:@"image.png"]; CGFloat scale = [[self.imageView.window screen] scale]; CGRect frame = CGRectMake(self.frame.origin.x *scale, self.frame.origin.y *scale, self.frame.size.width*scale, self.frame.size.width*scale); // temporarily remove the circleLayer CALayer *circleLayer = self.circleLayer; [self.circleLayer removeFromSuperlayer]; // render the clipped image UIGraphicsBeginImageContextWithOptions(self.imageView.frame.size, NO, 0.0); CGContextRef context = UIGraphicsGetCurrentContext(); if ([self.imageView respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) { // if iOS 7, just draw it [self.imageView drawViewHierarchyInRect:self.imageView.bounds afterScreenUpdates:YES]; } else { // if pre iOS 7, manually clip it CGContextAddRect(context, self.frame); CGContextClip(context); [self.imageView.layer renderInContext:context]; } // capture the image and close the context UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // add the circleLayer back [self.imageView.layer addSublayer:circleLayer]; // crop the image NSLog(@"circle fram %@",NSStringFromCGRect(frame)); NSLog(@"self fram %@",NSStringFromCGRect(self.frame)); CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], frame); UIImage *croppedImage = [UIImage imageWithCGImage:imageRef]; // save the image NSData *data = UIImagePNGRepresentation(croppedImage); [data writeToFile:path atomically:YES]; // tell the user we're done [[[UIAlertView alloc] initWithTitle:nil message:@"Saved" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil] show]; } #pragma mark - Gesture recognizers - (void)handlePan:(UIPanGestureRecognizer *)gesture { static CGPoint oldCenter; CGPoint tranlation = [gesture translationInView:gesture.view]; if (gesture.state == UIGestureRecognizerStateBegan) { oldCenter = self.circleCenter; } CGPoint newCenter = CGPointMake(oldCenter.x + tranlation.x, oldCenter.y + tranlation.y); [self updateCirclePathAtLocation:newCenter radius:self.circleRadius]; } - (void)handlePinch:(UIPinchGestureRecognizer *)gesture { static CGFloat oldRadius; CGFloat scale = [gesture scale]; if (gesture.state == UIGestureRecognizerStateBegan) { oldRadius = self.circleRadius; } CGFloat newRadius = oldRadius * scale; [self updateCirclePathAtLocation:self.circleCenter radius:newRadius]; } #pragma mark - UIGestureRecognizerDelegate - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { if ((gestureRecognizer == self.pan && otherGestureRecognizer == self.pinch) || (gestureRecognizer == self.pinch && otherGestureRecognizer == self.pan)) { return YES; } return NO; } @end 
 #import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet UIImageView *crppedImageView; @property (nonatomic) CGFloat circleRadius; @property (nonatomic) CGPoint circleCenter; @property (nonatomic, weak) CAShapeLayer *maskLayer; @property (nonatomic, weak) CAShapeLayer *maskSubLayer; @property (nonatomic, weak) CAShapeLayer *circleLayer; @property (nonatomic, weak) UIPinchGestureRecognizer *pinch; @property (nonatomic, weak) UIPanGestureRecognizer *pan; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // create layer mask for the image CAShapeLayer *maskLayer = [CAShapeLayer layer]; CGRect maskRect = self.imageView.frame; // Create a path with the rectangle in it. CGPathRef path = CGPathCreateWithRect(maskRect, NULL); // Set the path to the mask layer. maskLayer.path = path; // maskLayer.fillRule = kCAFillRuleEvenOdd; // maskLayer.fillColor = [UIColor blueColor].CGColor; // maskLayer.opacity = 0.5; self.imageView.layer.mask = maskLayer; self.maskLayer = maskLayer; CAShapeLayer *maskLayer1 = [CAShapeLayer layer]; CGRect maskRect1 = self.imageView.frame; // Create a path with the rectangle in it. CGPathRef path1 = CGPathCreateWithRect(maskRect1, NULL); // Set the path to the mask layer. maskLayer1.path = path1; [self.imageView.layer.mask addSublayer:maskLayer1]; self.maskSubLayer = maskLayer1; // create shape layer for circle we'll draw on top of image (the boundary of the circle) CAShapeLayer *circleLayer = [CAShapeLayer layer]; circleLayer.lineWidth = 3.0; circleLayer.fillColor = [[UIColor blueColor] CGColor]; circleLayer.fillRule = kCAFillRuleEvenOdd; circleLayer.opacity = 0.5; circleLayer.strokeColor = [[UIColor blackColor] CGColor]; [self.imageView.layer addSublayer:circleLayer]; self.circleLayer = circleLayer; // create circle path [self updateCirclePathAtLocation:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0) radius:self.view.bounds.size.width * 0.30]; // create pan gesture UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; pan.delegate = self; [self.imageView addGestureRecognizer:pan]; self.imageView.userInteractionEnabled = YES; self.pan = pan; // create pan gesture UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)]; pinch.delegate = self; [self.view addGestureRecognizer:pinch]; self.pinch = pinch; } - (void)updateCirclePathAtLocation:(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]; UIBezierPath *path1 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.imageView.bounds.size.width, self.imageView.bounds.size.height) cornerRadius:0]; [path1 appendPath:path]; [path1 setUsesEvenOddFillRule:YES]; self.maskSubLayer.path = [path1 CGPath]; self.circleLayer.path = [path1 CGPath]; } - (IBAction)didTouchUpInsideSaveButton:(id)sender { NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; NSString *path = [documentsPath stringByAppendingPathComponent:@"image.png"]; CGFloat scale = [[self.imageView.window screen] scale]; CGFloat radius = self.circleRadius * scale; CGPoint center = CGPointMake(self.circleCenter.x * scale, self.circleCenter.y * scale); CGRect frame = CGRectMake(center.x - radius, center.y - radius, radius * 2.0, radius * 2.0); // temporarily remove the circleLayer CALayer *circleLayer = self.circleLayer; [self.circleLayer removeFromSuperlayer]; // render the clipped image UIGraphicsBeginImageContextWithOptions(self.imageView.frame.size, NO, 0.0); CGContextRef context = UIGraphicsGetCurrentContext(); if ([self.imageView respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) { // if iOS 7, just draw it [self.imageView drawViewHierarchyInRect:self.imageView.bounds afterScreenUpdates:YES]; } else { // if pre iOS 7, manually clip it CGContextAddArc(context, self.circleCenter.x, self.circleCenter.y, self.circleRadius, 0, M_PI * 2.0, YES); CGContextClip(context); [self.imageView.layer renderInContext:context]; } // capture the image and close the context UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // add the circleLayer back [self.imageView.layer addSublayer:circleLayer]; // crop the image CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], frame); UIImage *croppedImage = [UIImage imageWithCGImage:imageRef]; _crppedImageView.layer.cornerRadius = _crppedImageView.frame.size.height /2; _crppedImageView.layer.masksToBounds = YES; _crppedImageView.layer.borderWidth = 0; self.crppedImageView.image = croppedImage; // save the image NSData *data = UIImagePNGRepresentation(croppedImage); [data writeToFile:path atomically:YES]; // tell the user we're done [[[UIAlertView alloc] initWithTitle:nil message:@"Saved" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil] show]; } #pragma mark - Gesture recognizers - (void)handlePan:(UIPanGestureRecognizer *)gesture { static CGPoint oldCenter; CGPoint tranlation = [gesture translationInView:gesture.view]; if (gesture.state == UIGestureRecognizerStateBegan) { oldCenter = self.circleCenter; } CGPoint newCenter = CGPointMake(oldCenter.x + tranlation.x, oldCenter.y + tranlation.y); [self updateCirclePathAtLocation:newCenter radius:self.circleRadius]; } - (void)handlePinch:(UIPinchGestureRecognizer *)gesture { static CGFloat oldRadius; CGFloat scale = [gesture scale]; if (gesture.state == UIGestureRecognizerStateBegan) { oldRadius = self.circleRadius; } CGFloat newRadius = oldRadius * scale; [self updateCirclePathAtLocation:self.circleCenter radius:newRadius]; } #pragma mark - UIGestureRecognizerDelegate - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { if ((gestureRecognizer == self.pan && otherGestureRecognizer == self.pinch) || (gestureRecognizer == self.pinch && otherGestureRecognizer == self.pan)) { return YES; } return NO; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end