Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save andreacipriani/74ea67db8f17673f1b8b to your computer and use it in GitHub Desktop.
Save andreacipriani/74ea67db8f17673f1b8b to your computer and use it in GitHub Desktop.
iOS: UIImagePickerController editing view circle overlay
/**
Credit: I've started by reading this SO question: http://stackoverflow.com/questions/20794187/uiimagepickercontroller-editing-view-circle-overlay-edited
Trick to add a circle view on image picker editing to facilitate circular cropping
Tested on iPhone4s, iPhone5, iPhone6, iPhone6+, iPad - iOS 7 and iOS 8 - on May 2015
**/
#pragma mark - UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([navigationController.viewControllers count] == 3 && ([[[[navigationController.viewControllers objectAtIndex:2] class] description] isEqualToString:@"PUUIImageViewController"] || [[[[navigationController.viewControllers objectAtIndex:2] class] description] isEqualToString:@"PLUIImageViewController"]))
{
[self addCircleOverlayToImagePicker:viewController];
}
}
#pragma mark - Circle overlay trick
-(void)addCircleOverlayToImagePicker:(UIViewController*)viewController
{
UIColor *circleColor = [UIColor clearColor];
UIColor *maskColor = [[UIColor blackColor] colorWithAlphaComponent:0.8];
CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
UIView *plCropOverlayCropView; //The default crop view, we wan't to hide it and show our circular one
UIView *plCropOverlayBottomBar; //On iPhone is the bar with "cancel" and "choose" button, on Ipad is an Image View with a label saying "Scale and move"
//Subviews hirearchy is different in iPad/iPhone:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
plCropOverlayCropView = [viewController.view.subviews objectAtIndex:1];
plCropOverlayBottomBar = [[[[viewController.view subviews] objectAtIndex:1] subviews] objectAtIndex:1];
//Protect against iOS changes...
if (! [[[plCropOverlayCropView class] description] isEqualToString:@"PLCropOverlay"]){
DDLogWarn(@"Image Picker with circle overlay: PLCropOverlay not found");
return;
}
if (! [[[plCropOverlayBottomBar class] description] isEqualToString:@"UIImageView"]){
DDLogWarn(@"Image Picker with circle overlay: PLCropOverlayBottomBar not found");
return;
}
}
else{
plCropOverlayCropView = [[[viewController.view.subviews objectAtIndex:1] subviews] firstObject];
plCropOverlayBottomBar = [[[[viewController.view subviews] objectAtIndex:1] subviews] objectAtIndex:1];
//Protect against iOS changes...
if (! [[[plCropOverlayCropView class] description] isEqualToString:@"PLCropOverlayCropView"]){
DDLogWarn(@"Image Picker with circle overlay: PLCropOverlayCropView not found");
return;
}
if (! [[[plCropOverlayBottomBar class] description] isEqualToString:@"PLCropOverlayBottomBar"]){
DDLogWarn(@"Image Picker with circle overlay: PLCropOverlayBottomBar not found");
return;
}
}
//It seems that everything is ok, we found the CropOverlayCropView and the CropOverlayBottomBar
plCropOverlayCropView.hidden = YES; //Hide default CropView
CAShapeLayer *circleLayer = [CAShapeLayer layer];
//Center the circleLayer frame:
UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0.0f, screenHeight/2 - screenWidth/2, screenWidth, screenWidth)];
circlePath.usesEvenOddFillRule = YES;
circleLayer.path = [circlePath CGPath];
circleLayer.fillColor = circleColor.CGColor;
//Mask layer frame: it begins on y=0 and ends on y = plCropOverlayBottomBar.origin.y
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, screenWidth, screenHeight- plCropOverlayBottomBar.frame.size.height) cornerRadius:0];
[maskPath appendPath:circlePath];
maskPath.usesEvenOddFillRule = YES;
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = maskPath.CGPath;
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.fillColor = maskColor.CGColor;
[viewController.view.layer addSublayer:maskLayer];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone){
//On iPhone add an hint label on top saying "scale and move" or whatever you want
UILabel *cropLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 10, screenWidth, 50)];
[cropLabel setText:NSLocalizedString(@"IMAGE_PICKER_CROP_LABEL", nil)];
[cropLabel setTextAlignment:NSTextAlignmentCenter];
[cropLabel setTextColor:[UIColor whiteColor]];
[viewController.view addSubview:cropLabel];
}
else{ //On iPad re-add the overlayBottomBar with the label "scale and move" because we set its parent to hidden (it's a subview of PLCropOverlay)
[viewController.view addSubview:plCropOverlayBottomBar];
}
}
@JaDenis
Copy link

JaDenis commented Mar 17, 2016

dude, change your "didShowViewController" delegate method to "willShowViewController" - no delay after that 👍

still looking into take Photo with Camera issue...

@DeepakSaki
Copy link

great its help me.

@DeepakSaki
Copy link

it is crash in swift 3.0 but successfully run in objective c in xcode 8.2.1 version.

@thuydao
Copy link

thuydao commented Apr 7, 2017

hi @andreacipriani, @JaDenis, thanks for code, i have problem that, i want to change frame of new overlay, but crop frame not change follow new overlay. Please help me for this case. New overlay of me:

UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(25.0f , screenHeight/2 - screenWidth/2, screenWidth-50, screenWidth-50)];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment