iOS Custom UIImagePickerController Camera Crop to circle - 在预览视图中 [英] iOS Custom UIImagePickerController Camera Crop to circle - in preview view

查看:10
本文介绍了iOS Custom UIImagePickerController Camera Crop to circle - 在预览视图中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

  • ImageManipulation.swift

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

  • Colors.swift

内容视图

 导入 SwiftUI结构内容视图:查看{@State 私有变量 isShowingPhotoSelectionSheet = false@State private var finalImage:UIImage?@State private var inputImage:UIImage?var body: 一些视图 {堆栈{如果 finalImage != nil {图片(uiImage:finalImage!).resizable().frame(宽度:100,高度:100).scaledToFill().aspectRatio(内容模式:.fit).clipShape(圆()).shadow(半径:4)} 别的 {图片(系统名称:person.crop.circle.fill").resizable().scaledToFill().frame(宽度:100,高度:100).aspectRatio(内容模式:.fit).foregroundColor(.systemGray2)}按钮(动作:{self.isShowingPhotoSelectionSheet = true}, 标签: {Text(换照片").foregroundColor(.systemRed).font(.footnote)})}.background(颜色.systemBackground).statusBar(隐藏:isShowingPhotoSelectionSheet).fullScreenCover(isPresented: $isShowingPhotoSelectionSheet, onDismiss: loadImage) {ImageMoveAndScaleSheet(croppedImage: $finalImage)}}函数加载图像(){守卫让 inputImage = inputImage else { return }最终图像 = 输入图像}}结构 ContentView_Previews: PreviewProvider {静态变量预览:一些视图 {内容视图()}}

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

ImagemoveAndScaleSheet

这是一个全屏模式,在打开状态栏时会隐藏.

 导入 SwiftUI结构 ImageMoveAndScaleSheet:查看 {@Environment(.presentationMode) varpresentationMode@State 私有变量 isShowingImagePicker = false///裁剪后的图像将发送回父视图.///应该是图片中定义的正方形的一部分///剪切圆的直径.见下文,切口圆有一个插图".价值///这是可以改变的.@Binding varcroppedImage:UIImage?///从ImagePicker接收输入图像.///我们需要在函数中计算并参考它的纵横比.@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///profileImage 是我们在这个视图上看到的.当从///ImapgePicker,它将调整大小以适合屏幕,///意味着它的宽度将匹配设备屏幕的宽度,///或其高度将匹配设备屏幕的高度.///这不适用于横向模式或 iPad.@State private var profileImage:图像?@State private var profileW: CGFloat = 0.0@State private var profileH: CGFloat = 0.0///缩放和拖动...@State private var currentAmount: CGFloat = 0@State private var finalAmount: CGFloat = 1@State private var currentPosition: CGSize = .zero@State private var newPosition: CGSize = .zero///我们跟踪图像移动的数量,以便在下面的函数中使用.@State private var Horizo​​ntalOffset:CGFloat = 0.0@State private var verticalOffset: CGFloat = 0.0var body: 一些视图 {ZStack {ZStack {颜色.黑色.不透明度(0.8)如果 profileImage != nil {个人资料图片?.resizable().scaleEffect(finalAmount + currentAmount).scaledToFill().aspectRatio(内容模式:.fit).offset(x: self.currentPosition.width, y: self.currentPosition.height)} 别的 {图片(系统名称:person.crop.circle.fill").resizable().scaleEffect(finalAmount + currentAmount).scaledToFill().aspectRatio(内容模式:.fit).foregroundColor(.systemGray2)}}长方形().fill(Color.black).opacity(0.55).mask(HoleShapeMask().fill(style: FillStyle(eoFill: true)))堆栈{Text((profileImage != nil) ? "Move and Scale" : "通过点击下面的图标选择照片").foregroundColor(.white).padding(.top, 50)间隔()堆栈{ZStack {堆栈{按钮(行动:{presentationMode.wrappedValue.dismiss()},标签:{文本(取消")})间隔()按钮(行动: {自我保存()PresentationMode.wrappedValue.dismiss()}){ 文本(保存") }.opacity((profileImage != nil) ? 1.0 : 0.2).disabled((profileImage != nil) ? false: true)}.padding(.horizo​​ntal).foregroundColor(.white)图像(系统名称:circle.fill").font(.custom("system", size: 45)).不透明度(0.9).foregroundColor(.white)图片(系统名称:photo.on.rectangle").imageScale(.medium).foregroundColor(.black).onTapGesture {isShowingImagePicker = true}}.padding(.bottom, 5)}}.填充()}.edgesIgnoringSafeArea(.all)//MARK: - 手势.手势(放大手势().onChanged { 金额self.currentAmount = 数量 - 1//重新定位图像()}.onEnded { 金额self.finalAmount += self.currentAmountself.currentAmount = 0重新定位图像()}).同时手势(拖动手势().onChanged { 中的值self.currentPosition = CGSize(宽度:value.translation.width + self.newPosition.width,高度:value.translation.height + self.newPosition.height)}.onEnded { 值在self.currentPosition = CGSize(宽度:value.translation.width + self.newPosition.width,高度:value.translation.height + self.newPosition.height)self.newPosition = self.currentPosition重新定位图像()}).同时手势(TapGesture(计数:2).onEnded({resetImageOriginAndScale()})).sheet(isPresented: $isShowingImagePicker, onDismiss: loadImage) {ImagePicker(图像:self.$inputImage).accentColor(Color.systemRed)}}//MARK: - 函数私人 func HoleShapeMask() ->小路 {让 rect = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)让 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))返回形状}///ImagePicker 关闭时调用.///我们要测量接收到的图像并确定纵横比.私人函数加载图像(){守卫让 inputImage = inputImage else { return }让 w = inputImage.size.width让 h = inputImage.size.heightprofileImage = Image(uiImage: inputImage)输入W = w输入H = h纵横比 = w/hresetImageOriginAndScale()}///profileImage 的大小将适合屏幕.///但是我们需要知道宽度和高度///设置相关的@State变量.///Douobke-tpping图片也会设置///因为它最初是在加载时调整大小的.私有函数 resetImageOriginAndScale() {withAnimation(.easeInOut){如果纵横比>=屏幕纵横{profileW = UIScreen.main.bounds.widthprofileH = profileW/theAspectRatio} 别的 {profileH = UIScreen.main.bounds.heightprofileW = profileH * theAspectRatio}当前金额 = 0最终金额 = 1当前位置 = .zero新位置 = .zero}}私人功能重新定位图像(){//屏幕宽度让 w = UIScreen.main.bounds.width如果纵横比 >屏幕方面{profileW = UIScreen.main.bounds.width * finalAmountprofileH = profileW/theAspectRatio} 别的 {profileH = UIScreen.main.bounds.height * finalAmountprofileW = profileH * theAspectRatio}水平偏移 = (profileW - w)/2垂直偏移 = (profileH - w)/2///防止用户放大太多.根据个别项目的需要进行调整.如果最终金额 >4.0 {带动画{最终金额 = 4.0}}///以下 if 语句使图像填充圆形切口.如果 profileW >= UIScreen.main.bounds.width {如果 newPosition.width >水平偏移{withAnimation(.easeInOut) {newPosition = CGSize(宽度:horizo​​ntalOffset + inset,高度:newPosition.height)currentPosition = CGSize(宽度:horizo​​ntalOffset + inset,高度:currentPosition.height)}}如果 newPosition.width <(水平偏移* -1){withAnimation(.easeInOut){newPosition = CGSize(width: (horizo​​ntalOffset * -1) - inset, height: newPosition.height)currentPosition = CGSize(width: (horizo​​ntalOffset * -1 - inset), height: currentPosition.height)}}} 别的 {withAnimation(.easeInOut) {newPosition = CGSize(宽度:0,高度:newPosition.height)currentPosition = CGSize(宽度:0,高度:newPosition.height)}}如果 profileH >= UIScreen.main.bounds.width {如果 newPosition.height >垂直偏移{withAnimation(.easeInOut){newPosition = CGSize(宽度:newPosition.width,高度:verticalOffset + inset)currentPosition = CGSize(宽度:newPosition.width,高度:verticalOffset + inset)}}如果 newPosition.height <( 垂直偏移 * -1) {withAnimation(.easeInOut){newPosition = CGSize(width: newPosition.width, height: (verticalOffset * -1) - inset)currentPosition = CGSize(width: newPosition.width, height: (verticalOffset * -1) - inset)}}} 别的 {withAnimation (.easeInOut){newPosition = CGSize(宽度:newPosition.width,高度:0)currentPosition = CGSize(宽度:newPosition.width,高度:0)}}if profileW <= UIScreen.main.bounds.width &&纵横比 >屏幕方面{resetImageOriginAndScale()}if profileH <= UIScreen.main.bounds.height &&纵横比屏幕方面{resetImageOriginAndScale()}}私人函数保存(){让 scale = (inputImage?.size.width)!/个人资料W让 xPos = ( ( ( profileW - UIScreen.main.bounds.width )/2 ) + inset + ( currentPosition.width * -1 ) ) * scale让 yPos = ( ( ( profileH - UIScreen.main.bounds.width )/2 ) + inset + ( currentPosition.height * -1 ) ) * scale让半径 = ( UIScreen.main.bounds.width - inset * 2 ) * 比例croppedImage = imageWithImage(image: inputImage!, croppedTo: CGRect(x: xPos, y: yPos, 宽度: 半径, 高度: 半径))///调试数学print("输入:w (inputW) h (inputH)")打印(配置文件:w (profileW)h (profileH)")print("X Origin: ( ( (profileW - UIScreen.main.bounds.width - inset )/2 ) + ( currentPosition.width * -1 ) )")print("Y 原点: ( ( (profileH - UIScreen.main.bounds.width - inset)/2 ) + ( currentPosition.height * -1 ) )")打印(比例:(比例)")打印(配置文件:(profileW)+ (profileH)")print("当前位置:(currentPosition.debugDescription)")打印(半径:(半径)")打印(x:(xPos),y:(yPos)")}让插图:CGFloat = 15让 screenAspect = UIScreen.main.bounds.width/UIScreen.main.bounds.height}

除了拖动和缩放手势之外,主要的外观和(可能是清理!)是功能.

  • HoleShapeMask()(不记得该代码在哪里,但我知道我在 SO 上得到了它.
  • repositionImage()(这里非常头疼)
  • save() 使用 ImageManipulation.swift 文件中的函数.

图像选择器

同样,这只是来自 Hacking With Swift.(谢谢保罗!)https://twitter.com/twostraws/

 导入 SwiftUI结构 ImagePicker: UIViewControllerRepresentable {@Environment(.presentationMode) varpresentationMode@Binding var 图像:UIImage?类协调器:NSObject,UINavigationControllerDelegate,UIImagePickerControllerDelegate {让父母:ImagePicker初始化(_父:ImagePicker){self.parent = 父母}func imagePickerController(_picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {如果让 uiImage = info[.originalImage] 为?UIImage {parent.image = uiImage}parent.presentationMode.wrappedValue.dismiss()}}func makeCoordinator() ->协调员{协调员(自)}func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) ->UIImagePickerController {让选择器 = UIImagePickerController()picker.delegate = context.coordinator退货选择器}func updateUIViewController(_uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {}}

ImageManipulation.swift

其中包含以下代码:

 导入 UIKitfunc imageWithImage(image: UIImage,croppedTo rect: CGRect) ->UIImage {UIGraphicsBeginImageContext(rect.size)让上下文 = UIGraphicsGetCurrentContext()让 drawRect = CGRect(x: -rect.origin.x, y: -rect.origin.y,宽度:image.size.width,高度:image.size.height)context?.clip(to: CGRect(x: 0, y: 0,宽度:rect.size.width,高度:rect.size.height))image.draw(在:drawRect)让 subImage = UIGraphicsGetImageFromCurrentImageContext()UIGraphicsEndImageContext()返回子图!}## 颜色.swift ##在 SwiftUI 中访问系统 UIColors 的便捷扩展:进口基金会导入 SwiftUI扩展颜色 {静态变量标签:颜色 {返回颜色(UIColor.label)}静态变量secondaryLabel:颜色{返回颜色(UIColor.secondaryLabel)}静态变量 tertiaryLabel:颜色 {返回颜色(UIColor.tertiaryLabel)}静态变量 quaternaryLabel:颜色 {返回颜色(UIColor.quaternaryLabel)}静态变量系统填充:颜色 {返回颜色(UIColor.systemFill)}静态变量secondarySystemFill:颜色{返回颜色(UIColor.secondarySystemFill)}静态变量 tertiarySystemFill: 颜色 {返回颜色(UIColor.tertiarySystemFill)}静态变量 quaternarySystemFill: 颜色 {返回颜色(UIColor.quaternarySystemFill)}静态变量系统背景:颜色 {返回颜色(UIColor.systemBackground)}静态变量辅助系统背景:颜色 {返回颜色(UIColor.secondarySystemBackground)}静态变量 tertiarySystemBackground: 颜色 {返回颜色(UIColor.tertiarySystemBackground)}静态 var systemGroupedBackground: 颜色 {返回颜色(UIColor.systemGroupedBackground)}静态变量secondarySystemGroupedBackground:颜色{返回颜色(UIColor.secondarySystemGroupedBackground)}静态变量 tertiarySystemGroupedBackground: 颜色 {返回颜色(UIColor.tertiarySystemGroupedBackground)}静态变量系统红色:颜色 {返回颜色(UIColor.systemRed)}静态变量 systemBlue: 颜色 {返回颜色(UIColor.systemBlue)}静态变量 systemPink: 颜色 {返回颜色(UIColor.systemPink)}静态 var systemTeal: 颜色 {返回颜色(UIColor.systemTeal)}静态变量 systemGreen: 颜色 {返回颜色(UIColor.systemGreen)}静态变量 systemIndigo: 颜色 {返回颜色(UIColor.systemIndigo)}静态变量 systemOrange: 颜色 {返回颜色(UIColor.systemOrange)}静态变量 systemPurple: 颜色 {返回颜色(UIColor.systemPurple)}静态变量系统黄色:颜色 {返回颜色(UIColor.systemYellow)}静态变量 systemGray: 颜色 {返回颜色(UIColor.systemGray)}静态变量 systemGray2: 颜色 {返回颜色(UIColor.systemGray2)}静态变量 systemGray3: 颜色 {返回颜色(UIColor.systemGray3)}静态变量 systemGray4: 颜色 {返回颜色(UIColor.systemGray4)}静态变量 systemGray5: 颜色 {返回颜色(UIColor.systemGray5)}静态变量 systemGray6: 颜色 {返回颜色(UIColor.systemGray6)}}

I'm using this code to make a custom camera crop:

UIImagePickerController editing view circle overlay

This works perfectly in camera roll but not taking photos

If I change [navigationController.viewControllers count] == 3 --> [navigationController.viewControllers count] == 1 works for camera too, but not in next view (preview view where you accept to use the photo)

Someone help me??

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

}

}

解决方案

Similar solution in SwiftUI, Swift 5

I banged my head on this for a long, long time, but finally have a solution that works.

I've not been coding in Swift or SwiftUI for long, and I absolutely welcome comments to improve this code. In some places, I've left in a bit of Debugging code you can uncomment. Wrapping my head around the math involved more trial and error than competent, well-thought out approaches!

Shortcomings in this code: first, it would be nice to open the Impage picker from ContentView() and then show my custom view. I don't know how to do that. Second, if there is already an image in the ContentView(), it would be nice to populate the image in the custom view. However, then the user may expect to be able to get the "original" image and move it and scale it. That would require more than is needed for this answer. Ie, do you want to save the original photo in some url / application folder as well as the cropped version? Or even save a dictionary with the original picture and the CGRect needed to recreate the cropped view? Third. there seems to be a bug if the selected photo is exactly the size of the screen (a screenshot); it can be scaled too low.

In a new SwiftUI lifecycle app, I have the following SwiftUI views:

This is what you'll get:

I also use this crucial solution for cropping:

  • ImageManipulation.swift

Finally, some of my code accesses system UIcolors so I use the extension in

  • Colors.swift

ContentView

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

Clicking / tapping on Change photo brings up the next view:

ImagemoveAndScaleSheet

This is a fullscreen modal which hides the statusbar while open.

    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
    }

Apart from the drag and scale gestures, the main things to look and (and probably clean up!) are the functions.

  • HoleShapeMask() (cannot remember where that code is, but I know I got it on SO.
  • repositionImage() (much headbanging here)
  • save() which uses the funciton in the ImageManipulation.swift file.

ImagePicker

Again, this is simply from Hacking With Swift. (Thanks 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

This contains the following code:

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


这篇关于iOS Custom UIImagePickerController Camera Crop to circle - 在预览视图中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆