iOS自定义UIImagePickerController相机预览视图中将照片裁剪成圆形

3
我正在使用这段代码来创建一个自定义的摄像头裁剪: UIImagePickerController 编辑视图圆形叠加 这在相机胶卷中运行得很完美,但拍照时则不行。
如果我将 [navigationController.viewControllers count] == 3 改为 [navigationController.viewControllers count] == 1,则相机也可以正常工作,但在下一个视图(预览视图,用于接受照片)中则不行。
请有人帮帮我?
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
    NSLog(@"Camara");
    UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init];
    imagePicker.allowsEditing = YES;
    imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
    imagePicker.delegate = self;
    self.isCamera = YES;

    [self presentViewController:imagePicker animated:YES completion:nil];

}else{
    NSLog(@"Carrete");
    UIImagePickerController *imagePickerController = [[UIImagePickerController alloc]init];
    imagePickerController.allowsEditing = YES;
    imagePickerController.delegate = self;
    imagePickerController.sourceType =  UIImagePickerControllerSourceTypePhotoLibrary;
    self.isCamera = NO;
    [self presentViewController:imagePickerController animated:YES completion:nil];
}

}

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
if (self.isCamera) {
    if ([navigationController.viewControllers count] == 1)
    {
        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];
    }

}else{
    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];
    }

}

}


1
你有找到任何解决方案吗?我也遇到了相机的问题。请在http://stackoverflow.com/questions/32768211/circular-cropper-camera-with-uiimagepickercontroller-ios上更新我。 - user100
4个回答

8

SwiftUI中类似的解决方案,Swift 5

我曾经为此苦苦钻研很长时间,但最终找到了可行的解决方案。

我没有在Swift或SwiftUI中编程很长时间,非常欢迎评论来改进这段代码。在某些地方,我留下了一些调试代码,你可以取消注释。理解涉及到的数学问题比具备能力和深思熟虑的方法更需要通过尝试和错误来实现!

本代码的缺陷: 第一,希望能够从ContentView()中打开图像选择器,然后显示我的自定义视图。我不知道该怎么做。 第二,如果ContentView()中已经有一张图像,将该图像填充到自定义视图中会更好。但是,用户可能希望能够获取“原始”图像并移动和缩放它。这需要比本答案所需更多的东西。例如,您是否想将原始照片保存在某个URL/应用程序文件夹中以及裁剪版本中?或者甚至保存一个包含原始图片和重建裁剪视图所需CGRect的字典?第三,如果选择的照片恰好与屏幕大小相同(屏幕截图),则它可能被缩放得太低。

在一个新的SwiftUI生命周期应用程序中,我有以下SwiftUI视图: 这就是您将获得的内容。

SwiftUI example of photo move and scale app SwiftUI circlemask screen for photo selection ImagePicker UIRepresentable in SwiftUI Selected Image in SwiftUI view Scaled Image in custom SwiftUI view Saved cropped and scqaled image for contact image

我还使用这个关键的解决方案进行裁剪:

  • ImageManipulation.swift

最后,我的一些代码访问系统UI颜色,所以我使用扩展功能

  • Colors.swift

内容视图

    import SwiftUI
    
    struct ContentView: View {
        
        @State private var isShowingPhotoSelectionSheet = false

        @State private var finalImage: UIImage?
        @State private var inputImage: UIImage?
        
        var body: some View {
            
            VStack {
                
                if finalImage != nil {
                    Image(uiImage: finalImage!)
                        .resizable()
                        .frame(width: 100, height: 100)
                        .scaledToFill()
                        .aspectRatio(contentMode: .fit)
                        .clipShape(Circle())
                        .shadow(radius: 4)
                } else {
                    Image(systemName: "person.crop.circle.fill")
                        .resizable()
                        .scaledToFill()
                        .frame(width: 100, height: 100)
                        .aspectRatio(contentMode: .fit)
                        .foregroundColor(.systemGray2)
                }
                Button (action: {
                    self.isShowingPhotoSelectionSheet = true
                }, label: {
                    Text("Change photo")
                        .foregroundColor(.systemRed)
                        .font(.footnote)
                })
            }
            .background(Color.systemBackground)
            .statusBar(hidden: isShowingPhotoSelectionSheet)
            .fullScreenCover(isPresented: $isShowingPhotoSelectionSheet, onDismiss: loadImage) {
                ImageMoveAndScaleSheet(croppedImage: $finalImage)
            }
        }
        
        func loadImage() {
            guard let inputImage = inputImage else { return }
            finalImage = inputImage
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }

点击/轻触更改照片会出现下一个视图:

ImagemoveAndScaleSheet

这是一个全屏模态,打开时隐藏状态栏。

    import SwiftUI
    
    struct ImageMoveAndScaleSheet: View {
        
        @Environment(\.presentationMode) var presentationMode
        
        @State private var isShowingImagePicker = false

        ///The croped image is what will will send back to the parent view.
        ///It should be the part of the image in the square defined by the
        ///cutout circle's diamter. See below, the cutout circle has an "inset" value
        ///which can be changed.
        @Binding var croppedImage: UIImage?

        ///The input image is received from the ImagePicker.
        ///We will need to calculate and refer to its aspectr ratio in the functions.
        @State private var inputImage: UIImage?
        @State private var inputW: CGFloat = 750.5556577
        @State private var inputH: CGFloat = 1336.5556577
        
        @State private var theAspectRatio: CGFloat = 0.0

        ///The profileImage is what wee see on this view. When added from the
        ///ImapgePicker, it will be sized to fit the screen,
        ///meaning either its width will match the width of the device's screen,
        ///or its height will match the height of the device screen.
        ///This is not suitable for landscape mode or for iPads.
        @State private var profileImage: Image?
        @State private var profileW: CGFloat = 0.0
        @State private var profileH: CGFloat = 0.0
        
        ///Zoom and Drag ...
        @State private var currentAmount: CGFloat = 0
        @State private var finalAmount: CGFloat = 1
        
        @State private var currentPosition: CGSize = .zero
        @State private var newPosition: CGSize = .zero
        
        ///We track of amount the image is moved for use in functions below.
        @State private var horizontalOffset: CGFloat = 0.0
        @State private var verticalOffset: CGFloat = 0.0
        
        var body: some View {
            
            ZStack {
                ZStack {
                    Color.black.opacity(0.8)
                    if profileImage != nil {
                        profileImage?
                            .resizable()
                            .scaleEffect(finalAmount + currentAmount)
                            .scaledToFill()
                            .aspectRatio(contentMode: .fit)
                            .offset(x: self.currentPosition.width, y: self.currentPosition.height)
                    } else {
                        Image(systemName: "person.crop.circle.fill")
                            .resizable()
                            .scaleEffect(finalAmount + currentAmount)
                            .scaledToFill()
                            .aspectRatio(contentMode: .fit)
                            .foregroundColor(.systemGray2)
                    }
                }
                Rectangle()
                    .fill(Color.black).opacity(0.55)
                    .mask(HoleShapeMask().fill(style: FillStyle(eoFill: true)))
                VStack {
                    Text((profileImage != nil) ? "Move and Scale" : "Select a Photo by tapping the icon below")
                        .foregroundColor(.white)
                        .padding(.top, 50)
                    Spacer()
                    HStack{
                        ZStack {
                            HStack {
                                Button(
                                    action: {presentationMode.wrappedValue.dismiss()},
                                    label: { Text("Cancel") })
                                Spacer()
                                Button(
                                    action: {
                                        self.save()
                                        presentationMode.wrappedValue.dismiss()
                                        
                                    })
                                    { Text("Save") }
                                    .opacity((profileImage != nil) ? 1.0 : 0.2)
                                    .disabled((profileImage != nil) ? false: true)
                                    
                            }
                            .padding(.horizontal)
                            .foregroundColor(.white)
                            Image(systemName: "circle.fill")
                                .font(.custom("system", size: 45))
                                .opacity(0.9)
                                .foregroundColor(.white)
                            Image(systemName: "photo.on.rectangle")
                                .imageScale(.medium)
                                .foregroundColor(.black)
                                .onTapGesture {
                                    isShowingImagePicker = true
                                }
                        }
                        .padding(.bottom, 5)
                    }
                }
                .padding()
            }
            .edgesIgnoringSafeArea(.all)
            
            //MARK: - Gestures
            
            .gesture(
                MagnificationGesture()
                    .onChanged { amount in
                        self.currentAmount = amount - 1
                        //                    repositionImage()
                    }
                    .onEnded { amount in
                        self.finalAmount += self.currentAmount
                        self.currentAmount = 0
                        repositionImage()
                    }
            )
            .simultaneousGesture(
                DragGesture()
                    .onChanged { value in
                        self.currentPosition = CGSize(width: value.translation.width + self.newPosition.width, height: value.translation.height + self.newPosition.height)
                    }
                    .onEnded { value in
                        self.currentPosition = CGSize(width: value.translation.width + self.newPosition.width, height: value.translation.height + self.newPosition.height)
                        self.newPosition = self.currentPosition
                        repositionImage()
                    }
            )
            .simultaneousGesture(
                TapGesture(count: 2)
                    .onEnded({
                        resetImageOriginAndScale()
                    })
            )
            .sheet(isPresented: $isShowingImagePicker, onDismiss: loadImage) {
                ImagePicker(image: self.$inputImage)
                    .accentColor(Color.systemRed)
            }
        }
        
        //MARK: - functions
        
        private func HoleShapeMask() -> Path {
            let rect = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
            let insetRect = CGRect(x: inset, y: inset, width: UIScreen.main.bounds.width - ( inset * 2 ), height: UIScreen.main.bounds.height - ( inset * 2 ))
            var shape = Rectangle().path(in: rect)
            shape.addPath(Circle().path(in: insetRect))
            return shape
        }
        
        ///Called when the ImagePicker is dismissed.
        ///We want to measure the image receoived and determine the aspect ratio.
        
        private func loadImage() {
            guard let inputImage = inputImage else { return }
            let w = inputImage.size.width
            let h = inputImage.size.height
            profileImage = Image(uiImage: inputImage)
            
            inputW = w
            inputH = h
            theAspectRatio = w / h
            
            resetImageOriginAndScale()
        }
        
        ///The profileImage will size to fit the screen.
        ///But we need to know the width and height
        ///to set the related @State variables.
        ///Douobke-tpping the image will also set it
        ///as it was sized originally upon loading.
        private func resetImageOriginAndScale() {
            withAnimation(.easeInOut){
                if theAspectRatio >= screenAspect {
                    profileW = UIScreen.main.bounds.width
                    profileH = profileW / theAspectRatio
                } else {
                    profileH = UIScreen.main.bounds.height
                    profileW = profileH * theAspectRatio
                }
                currentAmount = 0
                finalAmount = 1
                currentPosition = .zero
                newPosition = .zero
            }
        }
        
        
        private func repositionImage() {
            
            //Screen width
            let w = UIScreen.main.bounds.width
            
            if theAspectRatio > screenAspect {
                profileW = UIScreen.main.bounds.width * finalAmount
                profileH = profileW / theAspectRatio
            } else {
                profileH = UIScreen.main.bounds.height * finalAmount
                profileW = profileH * theAspectRatio
            }

            horizontalOffset = (profileW - w ) / 2
            verticalOffset = ( profileH - w ) / 2
            
            
            ///Keep the user from zooming too far in. Adjust as required by the individual project.
            if finalAmount > 4.0 {
                withAnimation{
                    finalAmount = 4.0
                }
            }
            
            ///The following if statements keep the image filling the circle cutout.
            if profileW >= UIScreen.main.bounds.width {
                
                if newPosition.width > horizontalOffset {
                    withAnimation(.easeInOut) {
                        newPosition = CGSize(width: horizontalOffset + inset, height: newPosition.height)
                        currentPosition = CGSize(width: horizontalOffset + inset, height: currentPosition.height)
                    }
                }
                
                if newPosition.width < ( horizontalOffset * -1) {
                    withAnimation(.easeInOut){
                        newPosition = CGSize(width: ( horizontalOffset * -1) - inset, height: newPosition.height)
                        currentPosition = CGSize(width: ( horizontalOffset * -1 - inset), height: currentPosition.height)
                    }
                }
            } else {
                
                withAnimation(.easeInOut) {
                    newPosition = CGSize(width: 0, height: newPosition.height)
                    currentPosition = CGSize(width: 0, height: newPosition.height)
                }
            }
            
            if profileH >= UIScreen.main.bounds.width {
                
                if newPosition.height > verticalOffset {
                    withAnimation(.easeInOut){
                        newPosition = CGSize(width: newPosition.width, height: verticalOffset + inset)
                        currentPosition = CGSize(width: newPosition.width, height: verticalOffset + inset)
                    }
                }
                
                if newPosition.height < ( verticalOffset * -1) {
                    withAnimation(.easeInOut){
                        newPosition = CGSize(width: newPosition.width, height: ( verticalOffset * -1) - inset)
                        currentPosition = CGSize(width: newPosition.width, height: ( verticalOffset * -1) - inset)
                    }
                }
            } else {
                
                withAnimation (.easeInOut){
                    newPosition = CGSize(width: newPosition.width, height: 0)
                    currentPosition = CGSize(width: newPosition.width, height: 0)
                }
            }
            
            if profileW <= UIScreen.main.bounds.width && theAspectRatio > screenAspect {
                resetImageOriginAndScale()
            }
            if profileH <= UIScreen.main.bounds.height && theAspectRatio < screenAspect {
                resetImageOriginAndScale()
            }
        }
        
        private func save() {
            
            let scale = (inputImage?.size.width)! / profileW
            
            let xPos = ( ( ( profileW - UIScreen.main.bounds.width ) / 2 ) + inset + ( currentPosition.width * -1 ) ) * scale
            let yPos = ( ( ( profileH - UIScreen.main.bounds.width ) / 2 ) + inset + ( currentPosition.height * -1 ) ) * scale
            let radius = ( UIScreen.main.bounds.width - inset * 2 ) * scale
            
            croppedImage = imageWithImage(image: inputImage!, croppedTo: CGRect(x: xPos, y: yPos, width: radius, height: radius))
            
            ///Debug maths
            print("Input: w \(inputW) h \(inputH)")
            print("Profile: w \(profileW) h \(profileH)")
            print("X Origin: \( ( ( profileW - UIScreen.main.bounds.width - inset ) / 2 ) + ( currentPosition.width  * -1 ) )")
            print("Y Origin: \( ( ( profileH - UIScreen.main.bounds.width - inset) / 2 ) + ( currentPosition.height  * -1 ) )")
            
            print("Scale: \(scale)")
            print("Profile:\(profileW) + \(profileH)" )
            print("Curent Pos: \(currentPosition.debugDescription)")
            print("Radius: \(radius)")
            print("x:\(xPos), y:\(yPos)")
        }
        
        let inset: CGFloat = 15
        let screenAspect = UIScreen.main.bounds.width / UIScreen.main.bounds.height
    }


除了拖动和缩放手势外,需要注意和(可能清理!)的主要内容是函数。
  • HoleShapeMask()(记不得代码在哪里,但我知道我在SO上得到了它。
  • repositionImage()(这里有很多头痛)
  • save()使用文件中的函数。

ImagePicker

同样来自Hacking With Swift。(谢谢Paul!)https://twitter.com/twostraws/

    import SwiftUI

    struct ImagePicker: UIViewControllerRepresentable {
        
        @Environment(\.presentationMode) var presentationMode
        @Binding var image: UIImage?
        
        class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
            let parent: ImagePicker
            
            init(_ parent: ImagePicker) {
                self.parent = parent
            }
            
            func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
                if let uiImage = info[.originalImage] as? UIImage {
                    parent.image = uiImage
                }
                parent.presentationMode.wrappedValue.dismiss()
            }
        }

        func makeCoordinator() -> Coordinator {
            Coordinator(self)
        }
        
        func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
            let picker = UIImagePickerController()
            picker.delegate = context.coordinator
            return picker
        }

        func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {

        }
    }

ImageManipulation.swift

这里包含以下代码:
    import UIKit

    func imageWithImage(image: UIImage, croppedTo rect: CGRect) -> UIImage {

        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()

        let drawRect = CGRect(x: -rect.origin.x, y: -rect.origin.y,
                              width: image.size.width, height: image.size.height)

        context?.clip(to: CGRect(x: 0, y: 0,
                                 width: rect.size.width, height: rect.size.height))

        image.draw(in: drawRect)

        let subImage = UIGraphicsGetImageFromCurrentImageContext()

        UIGraphicsEndImageContext()
        return subImage!
    }

## Colors.swift ##

A handy extension to access system UIColors in SwiftUI:

    import Foundation
    import SwiftUI

    extension Color {

        static var label: Color {
            return Color(UIColor.label)
        }

        static var secondaryLabel: Color {
            return Color(UIColor.secondaryLabel)
        }

        static var tertiaryLabel: Color {
            return Color(UIColor.tertiaryLabel)
        }

        static var quaternaryLabel: Color {
            return Color(UIColor.quaternaryLabel)
        }

        static var systemFill: Color {
            return Color(UIColor.systemFill)
        }

        static var secondarySystemFill: Color {
            return Color(UIColor.secondarySystemFill)
        }

        static var tertiarySystemFill: Color {
            return Color(UIColor.tertiarySystemFill)
        }

        static var quaternarySystemFill: Color {
            return Color(UIColor.quaternarySystemFill)
        }

        static var systemBackground: Color {
               return Color(UIColor.systemBackground)
        }

        static var secondarySystemBackground: Color {
            return Color(UIColor.secondarySystemBackground)
        }

        static var tertiarySystemBackground: Color {
            return Color(UIColor.tertiarySystemBackground)
        }

        static var systemGroupedBackground: Color {
            return Color(UIColor.systemGroupedBackground)
        }

        static var secondarySystemGroupedBackground: Color {
            return Color(UIColor.secondarySystemGroupedBackground)
        }

        static var tertiarySystemGroupedBackground: Color {
            return Color(UIColor.tertiarySystemGroupedBackground)
        }

        static var systemRed: Color {
            return Color(UIColor.systemRed)
        }

        static var systemBlue: Color {
            return Color(UIColor.systemBlue)
        }

        static var systemPink: Color {
            return Color(UIColor.systemPink)
        }

        static var systemTeal: Color {
            return Color(UIColor.systemTeal)
        }

        static var systemGreen: Color {
            return Color(UIColor.systemGreen)
        }

        static var systemIndigo: Color {
            return Color(UIColor.systemIndigo)
        }

        static var systemOrange: Color {
            return Color(UIColor.systemOrange)
        }

        static var systemPurple: Color {
            return Color(UIColor.systemPurple)
        }

        static var systemYellow: Color {
            return Color(UIColor.systemYellow)
        }

        static var systemGray: Color {
            return Color(UIColor.systemGray)
        }

        static var systemGray2: Color {
            return Color(UIColor.systemGray2)
        }

        static var systemGray3: Color {
            return Color(UIColor.systemGray3)
        }

        static var systemGray4: Color {
            return Color(UIColor.systemGray4)
        }

        static var systemGray5: Color {
            return Color(UIColor.systemGray5)
        }

        static var systemGray6: Color {
            return Color(UIColor.systemGray6)
        }
        
    }



1
以上答案现在已经稍加完善,并可在此处找到:https://github.com/Rillieux/PhotoSelectAndCrop - Rillieux
1
谢谢!非常好的例子! - Viktor Golubenkov
PhotoSelectAndCrop的2.0版本现在有一个演示应用程序,供那些想要使用它的人使用:https://github.com/Rillieux/PhotoSelectAndCropDemo - Rillieux

1
extension ProfileViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {

func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
    guard imagePickerController?.sourceType == .camera else {
        return
    }

    guard let view = viewController.view.subviews(deep: true, where: {
        String(describing: type(of:$0)) == "CAMPreviewView"
    }).first else {
        return
    }

    viewController.view.layoutIfNeeded()

    let camPreviewBounds = view.bounds
    let circleRect = CGRect(
        x: camPreviewBounds.minX + (camPreviewBounds.width - 320) * 0.5,
        y: camPreviewBounds.minY + (camPreviewBounds.height - 320) * 0.5,
        width: 320,
        height: 320
    )

    let path = UIBezierPath(roundedRect: camPreviewBounds, cornerRadius: 0)
    path.append(UIBezierPath(ovalIn: circleRect))

    let layer = CAShapeLayer()
    layer.path = path.cgPath
    layer.fillRule = CAShapeLayerFillRule.evenOdd;
    layer.fillColor = UIColor.black.cgColor
    layer.opacity = 0.8;

    view.layer.addSublayer(layer)
}
}

0

虽然我相信我的回复可能有点晚了,但最终我将我的解决方案与这个链接混合在一起:https://gist.github.com/hamin/e8c6dfe00d9c81375f3e,其中:

  1. 为了使叠加层在相机上正常工作,我监听了通知(拍摄和拒绝图片),以添加或删除圆形叠加层。

  2. 保留了上述提到的解决方案,在需要循环遍历 UINavigationController 并在请求时绘制圆形叠加层的情况下使用。

总之,请查看下面用 Swift 编写的我的解决方案:

public class CustomPicture: NSObject {
//MARK: - Properties
private var myPickerController: UIImagePickerController?
private var plCropOverlayBottomBar: UIView?
private var customLayer: CAShapeLayer?

//MARK: - Constants
private let screenHeight = UIScreen.mainScreen().bounds.size.height
private let screenWidth = UIScreen.mainScreen().bounds.size.width
private let kCameraNotificationIrisAnimationEnd = "_UIImagePickerControllerUserDidCaptureItem"
private let kCameraNotificationUserRejection = "_UIImagePickerControllerUserDidRejectItem"
private let kPUUIImageViewController = "PUUIImageViewController"
private let kPLUIImageViewController = "PLUIImageViewController"
private let kPLCropOverlayCropView = "PLCropOverlayCropView"
private let kPLCropOverlayBottomBar = "PLCropOverlayBottomBar"

//MARK: - Overrides
deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

//MARK: - Privates
private func camera() {
    listenToCameraNotifications()
    let myPickerController = UIImagePickerController()

    myPickerController.delegate = self
    myPickerController.sourceType = .Camera
    myPickerController.allowsEditing = true

    self.myPickerController = myPickerController

    self.navigationController?.presentViewController(myPickerController, animated: true, completion: nil)
}

private func listenToCameraNotifications() {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(cameraNotificationIrisEnd), name: kCameraNotificationIrisAnimationEnd, object: nil)

    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(cameraNotificationRejected), name: kCameraNotificationUserRejection, object: nil)
}

private func photoLibrary() {
    let myPickerController = UIImagePickerController()

    myPickerController.delegate = self
    myPickerController.allowsEditing = true
    myPickerController.sourceType = .PhotoLibrary

    self.myPickerController = myPickerController

    self.navigationController?.presentViewController(myPickerController, animated: true, completion: nil)
}

//MARK: - Selector
/**
 Listen to notification sent after reject button has been touched
 */
func cameraNotificationRejected() {
    customLayer!.removeFromSuperlayer()
    plCropOverlayBottomBar!.removeFromSuperview()
}

/**
 Listen to notification sent after picture has been taken
 */
func cameraNotificationIrisEnd() {
    addCircleOverlay(viewController: self.myPickerController!)
}
}

extension CustomPicture: UINavigationControllerDelegate {
//MARK: - Override
public func navigationController(navigationController: UINavigationController, willShowViewController: UIViewController, animated: Bool) {
    if isImageViewer(navigationController: navigationController) {
        addCircleOverlay(viewController: willShowViewController)
    }
}

//MARK: - Private
private func addCircleOverlay(viewController viewController: UIViewController) {
    hidePLCropOverlay(view: viewController.view)
    setPLCropOverlayBottomBar(view: viewController.view)
    setCustomLayer(viewController: viewController)
}

private func getCirclePath() -> UIBezierPath {
    let circlePath = UIBezierPath(ovalInRect: CGRectMake(0, screenHeight / 2 - screenWidth / 2, screenWidth, screenWidth))
    circlePath.usesEvenOddFillRule = true

    let circleLayer = CAShapeLayer()
    circleLayer.path = circlePath.CGPath
    circleLayer.fillColor = UIColor.clearColor().CGColor

    return circlePath
}

private func getMaskPath(screenWidth screenWidth: CGFloat, screenHeight: CGFloat, circlePath: UIBezierPath) -> UIBezierPath {
    let maskPath = UIBezierPath(roundedRect: CGRectMake(0, 0, screenWidth, screenHeight), cornerRadius: 0)
    maskPath.appendPath(circlePath)
    maskPath.usesEvenOddFillRule = true

    return maskPath
}

private func hidePLCropOverlay(view view: UIView) {
    for myView in view.subviews {
        if myView.isKindOfClass(NSClassFromString(kPLCropOverlayCropView)!) {
            myView.hidden = true
            break
        } else {
            hidePLCropOverlay(view: myView as UIView)
        }
    }
}

private func isImageViewer(navigationController navigationController: UINavigationController) -> Bool {
    if (navigationController.viewControllers.count == 3 &&
        (navigationController.viewControllers[2].dynamicType.description() == kPUUIImageViewController ||
            navigationController.viewControllers[2].dynamicType.description() == kPLUIImageViewController)) {

        return true
    }

    return false
}

private func setPLCropOverlayBottomBar(view view: UIView) {
    for myView in view.subviews {
        if myView.isKindOfClass(NSClassFromString(kPLCropOverlayBottomBar)!) {
            plCropOverlayBottomBar = myView
            break
        }
        else {
            savePLCropOverlayBottomBar(view: myView as UIView)
        }
    }
}

private func setCustomLayer(viewController viewController: UIViewController) {
    let circlePath = getCirclePath()
    let maskPath = getMaskPath(screenWidth: screenWidth, screenHeight: screenHeight, circlePath: circlePath)
    let maskLayer = CAShapeLayer()
    maskLayer.path = maskPath.CGPath
    maskLayer.fillRule = kCAFillRuleEvenOdd
    maskLayer.fillColor = UIColor.blackColor().colorWithAlphaComponent(0.8).CGColor
    customLayer = maskLayer

    viewController.view.layer.addSublayer(customLayer!)
    viewController.view.addSubview(plCropOverlayBottomBar!) // put back overlayBottomBar once we set its parent to hidden (subview of PLCropOverlay)
}
}

0

这里有一个解决方案,可能会帮助您创建裁剪叠加层:

- (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];
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接