UIImagePickerController编辑视图圆形覆盖

我已经能够完成我一直想要完成的工作了,那就是复制iOS内置的圆形照片裁剪器,用于内置的联系人应用程序。 然而,我坚持试图让我的CAShapeLayers正确的做出。 我试图做一个透明的320像素直径的圆圈,其余的视图充满了0.9 alpha黑色背景。 圆形和矩形是在正确的位置,但是,圆形是不完全透明的,就像我需要的那样。

我失去了如何解决这个问题。 我感谢您的帮助! 代码和屏幕截图:

 - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { if ([navigationController.viewControllers count] == 3) { CGRect screenRect = [[UIScreen mainScreen] bounds]; CGFloat screenHeight = screenRect.size.height; UIView *plCropOverlay = [[[viewController.view.subviews objectAtIndex:1]subviews] objectAtIndex:0]; plCropOverlay.hidden = YES; CAShapeLayer *circleLayer = [CAShapeLayer layer]; if (screenHeight == 568) { [circleLayer setPosition:CGPointMake(0.0f,124.0f)]; } else { [circleLayer setPosition:CGPointMake(0.0f,80.0f)]; } UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(0.0f, 0.0f, 320.0f, 320.0f)]; [circleLayer setPath:[path CGPath]]; [circleLayer setFillColor:[[UIColor whiteColor] CGColor]]; circleLayer.opacity = 0.7f; // Set to 0.7f to show for screenshot purposes; setting to 0.0 would make it invisible and blend in with the below rectangleLayer. CAShapeLayer *rectangleLayer = [CAShapeLayer layer]; UIBezierPath *path2 = [UIBezierPath bezierPathWithRect:CGRectMake(0.0f, 0.0f, 320.0f, screenHeight - 72)]; [rectangleLayer setPath:[path2 CGPath]]; [rectangleLayer setFillColor:[[UIColor blackColor] CGColor]]; [rectangleLayer setOpacity:0.9f]; [rectangleLayer addSublayer:circleLayer]; [[viewController.view layer] addSublayer:rectangleLayer]; UILabel *moveLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 10, 320, 50)]; [moveLabel setText:@"Move and Scale"]; [moveLabel setTextAlignment:NSTextAlignmentCenter]; [moveLabel setTextColor:[UIColor whiteColor]]; [viewController.view addSubview:moveLabel]; } } 

在这里输入图像说明

解决的代码:

 - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { if ([navigationController.viewControllers count] == 3) { CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height; UIView *plCropOverlay = [[[viewController.view.subviews objectAtIndex:1]subviews] objectAtIndex:0]; plCropOverlay.hidden = YES; int position = 0; if (screenHeight == 568) { position = 124; } else { position = 80; } CAShapeLayer *circleLayer = [CAShapeLayer layer]; UIBezierPath *path2 = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(0.0f, position, 320.0f, 320.0f)]; [path2 setUsesEvenOddFillRule:YES]; [circleLayer setPath:[path2 CGPath]]; [circleLayer setFillColor:[[UIColor clearColor] CGColor]]; UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 320, screenHeight-72) cornerRadius:0]; [path appendPath:path2]; [path setUsesEvenOddFillRule:YES]; CAShapeLayer *fillLayer = [CAShapeLayer layer]; fillLayer.path = path.CGPath; fillLayer.fillRule = kCAFillRuleEvenOdd; fillLayer.fillColor = [UIColor blackColor].CGColor; fillLayer.opacity = 0.8; [viewController.view.layer addSublayer:fillLayer]; UILabel *moveLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 10, 320, 50)]; [moveLabel setText:@"Move and Scale"]; [moveLabel setTextAlignment:NSTextAlignmentCenter]; [moveLabel setTextColor:[UIColor whiteColor]]; [viewController.view addSubview:moveLabel]; } } 

我已经更改@ aviatorken89的代码,因为它不适用于iPhone 6/6 +和iPad。 现在,它应该适用于任何iPhone的屏幕尺寸,也适用于iPad! 在iOS 7和iOS 8上testing

所有这些方法都不是很可靠,因为它们基于图像选取器子视图层次结构,当然苹果可以改变它。 我试图尽可能保护代码,以防止未来iOS版本发生可能的崩溃。

我会尽力让我的解决scheme更新: https : //gist.github.com/andreacipriani/74ea67db8f17673f1b8b

这里是代码:

 - (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]; } } -(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 overlay view, we wan't to hide it and show our circular one UIView *plCropOverlayBottomBar; //On iPhone this is the bar with "cancel" and "choose" buttons, on Ipad it's 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"]){ DLog(@"Warning - Image Picker with circle overlay: PLCropOverlay not found"); return; } if (! [[[plCropOverlayBottomBar class] description] isEqualToString:@"UIImageView"]){ DLog(@"Warning - Image Picker with circle overlay: BottomBar 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:@"Scale and move"]; //You should localize it [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]; } } 

Swift 3版本(也有相机拍摄的照片的圆润编辑层):

 // Insert this code to your view controller private var editLayer: CAShapeLayer! private var label: UILabel! override func viewDidLoad() { super.viewDidLoad() // Rounded edit layer navigationController?.delegate = self NotificationCenter.default.addObserver(self, selector: #selector(pictureCaptured), name: NSNotification.Name(rawValue: "_UIImagePickerControllerUserDidCaptureItem"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(pictureRejected), name: NSNotification.Name(rawValue: "_UIImagePickerControllerUserDidRejectItem"), object: nil) } @objc private func pictureCaptured() { addRoundedEditLayer(to: ...your UIImagePickerController..., forCamera: true) } @objc private func pictureRejected() { editLayer.removeFromSuperlayer() label.removeFromSuperview() } deinit { NotificationCenter.default.removeObserver(self) } // MARK: Navigation controller delegate func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { // Image picker in edit mode if let imageVC = NSClassFromString("PUUIImageViewController") { if viewController.isKind(of: imageVC) { addRoundedEditLayer(to: viewController, forCamera: false) } } } private func addRoundedEditLayer(to viewController: UIViewController, forCamera: Bool) { hideDefaultEditOverlay(view: viewController.view) // Circle in edit layer - y position let bottomBarHeight: CGFloat = 72.0 let position = (forCamera) ? viewController.view.center.y - viewController.view.center.x - bottomBarHeight/2 : viewController.view.center.y - viewController.view.center.x let viewWidth = viewController.view.frame.width let viewHeight = viewController.view.frame.height let emptyShapePath = UIBezierPath(ovalIn: CGRect(x: 0, y: position, width: viewWidth, height: viewWidth)) emptyShapePath.usesEvenOddFillRule = true let filledShapePath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: viewWidth, height: viewHeight - bottomBarHeight), cornerRadius: 0) filledShapePath.append(emptyShapePath) filledShapePath.usesEvenOddFillRule = true editLayer = CAShapeLayer() editLayer.path = filledShapePath.cgPath editLayer.fillRule = kCAFillRuleEvenOdd editLayer.fillColor = UIColor.black.cgColor editLayer.opacity = 0.5 viewController.view.layer.addSublayer(editLayer) // Move and Scale label label = UILabel(frame: CGRect(x: 0, y: 10, width: viewWidth, height: 50)) label.text = "Move and Scale" label.textAlignment = .center label.textColor = UIColor.white viewController.view.addSubview(label) } private func hideDefaultEditOverlay(view: UIView) { for subview in view.subviews { if let cropOverlay = NSClassFromString("PLCropOverlayCropView") { if subview.isKind(of: cropOverlay) { subview.isHidden = true break } else { hideDefaultEditOverlay(view: subview) } } } } 

为了从相机做到这一点,请尝试使用cameraOverlayView并设置您自己的视图。 这只会从相机,而不是照片库时才起作用。