【问题标题】:iOS Custom UIImagePickerController Camera Crop to circle - in preview viewiOS Custom UIImagePickerController Camera Crop to circle - 在预览视图中
【发布时间】:2015-02-06 12:26:58
【问题描述】:

我正在使用此代码进行自定义相机裁剪:

UIImagePickerController editing view circle overlay

这在相机胶卷中完美运行,但不能拍照

如果我更改 [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];
    }

}

}

【问题讨论】:

标签: ios camera uiimagepickercontroller


【解决方案1】:

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

我为此苦苦思考了很久很久,但终于找到了一个可行的解决方案。

我很久没有使用 Swift 或 SwiftUI 进行编码了,我绝对欢迎 cmets 改进此代码。在某些地方,我留下了一些调试代码,您可以取消注释。围绕数学思考所涉及的试验和错误比有能力、经过深思熟虑的方法更多!

此代码中的缺点首先,最好从 ContentView() 打开图像选择器,然后显示我的自定义视图。我不知道该怎么做。 其次,如果 ContentView() 中已经有图像,最好在自定义视图中填充图像。然而,用户可能期望能够获得“原始”图像并移动它并对其进行缩放。这需要的不仅仅是这个答案所需要的。即,您是否要将原始照片保存在某个 url / 应用程序文件夹以及裁剪版本中?或者甚至保存包含原始图片和重新创建裁剪视图所需的 CGRect 的字典? 第三。如果所选照片正好是屏幕的大小(屏幕截图),则似乎存在错误;它可以缩放得太低。

在一个新的 SwiftUI 生命周期应用中,我有以下 SwiftUI 视图:

这就是你会得到的:

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

  • ImageManipulation.swift

最后,我的一些代码访问了系统 UIcolors,所以我在中使用了扩展

  • 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()
        }
    }

点击/点击Change photo 会弹出下一个视图:

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() 使用ImageManipulation.swift 文件中的函数。

图像选择器

同样,这只是来自 Hacking With Swift。 (谢谢保罗!)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)
        }
        
    }


【讨论】:

【解决方案2】:
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)
}
}

【讨论】:

    【解决方案3】:

    虽然我认为我的回复可能为时已晚,但我最终还是将我的解决方案与这个解决方案混合在一起: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)
    }
    }
    

    【讨论】:

      【解决方案4】:

      这是可能帮助您创建裁剪覆盖的解决方案:-

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-20
        相关资源
        最近更新 更多