首页 > 解决方案 > 如何在用户调整大小和重新定位后从 SwiftUI 2.0 滚动视图中保存完成的图像

问题描述

使用 Swift 2.0,我希望在用户从滚动视图 (ZoomScrollView) 中选择他们希望如何在框架中看到它之后,找到一种方法来捕获调整大小的图像。

我知道 Swift 有一些复杂的例子,但我希望在 Swift 2.0 中找到一种更简单的方法来捕捉它。在我所有的搜索中,我都听说过使用 ZStack 和一些遮罩或覆盖,但找不到一个简单的好例子。

我希望有人可以用 ZStack、掩码等更新我的示例,以及如何提取图像以保存或提供更好的示例。

import SwiftUI

struct ContentView: View {
    @Environment(\.presentationMode) var presentationMode

    @State var isAccepted: Bool = false
    
    @State var isShowingImagePicker = false
    @State var isShowingActionPicker = false
    
    @State var sourceType:UIImagePickerController.SourceType = .camera
    @State var image:UIImage?
    
    var body: some View {
        HStack {
            Color(UIColor.systemYellow).frame(width: 8)
            VStack(alignment: .leading) {
                HStack {
                    Spacer()
                    VStack {
                        if image != nil {
                            ZoomScrollView {
                              Image(uiImage: image!)
                                .resizable()
                                .scaledToFit()
                            }
                            .frame(width: 300, height: 300, alignment: .center)
                            .clipShape(Circle())
                            
                        } else {
                            Image(systemName: "person.crop.circle")
                                .resizable()
                                .font(.system(size: 32, weight: .light))
                                .frame(width: 300, height: 300, alignment: .center)
                                .cornerRadius(180)
                                .foregroundColor(Color(.systemGray))
                                .clipShape(Circle())
                                
                        }
                    }
                    Spacer()
                }
                Spacer()
                
                HStack {
                    Button(action: {
                        self.isShowingActionPicker = true
                    }, label: {
                        Text("Select Image")
                            .foregroundColor(.blue)
                    })
                    .frame(width: 130)
                    .actionSheet(isPresented: $isShowingActionPicker, content: {
                        ActionSheet(title: Text("Select a profile avatar picture"), message: nil, buttons: [
                            .default(Text("Camera"), action: {
                                self.isShowingImagePicker = true
                                self.sourceType = .camera
                            }),
                            .default(Text("Photo Library"), action: {
                                self.isShowingImagePicker = true
                                self.sourceType = .photoLibrary
                            }),
                            .cancel()
                        ])
                    })
                    .sheet(isPresented: $isShowingImagePicker) {
                        imagePicker(image: $image, isShowingImagePicker: $isShowingImagePicker ,sourceType: self.sourceType)
                    }
                    
                    Spacer()
                    
                    // Save button
                    Button(action: {

                        // Save Image here...  print for now just see if file dimensions are the right size
                        print("saved: ", image!)
                        self.presentationMode.wrappedValue.dismiss()
                    }
                    ) {
                        HStack {
                            Text("Save").foregroundColor(isAccepted ? .gray : .blue)
                        }
                    }
                    .frame(width: 102)
                    .padding(.top)
                    .padding(.bottom)
                    //.buttonStyle(RoundedCorners())
                    .disabled(isAccepted)  // Disable if if already isAccepted is true
                }
            }
            Spacer()
            Color(UIColor.systemYellow).frame(width: 8)
        }
        .padding(.top, UIApplication.shared.windows.first?.safeAreaInsets.top)
        .background(Color(UIColor.systemYellow))
    }
}

struct ZoomScrollView<Content: View>: UIViewRepresentable {
  private var content: Content

  init(@ViewBuilder content: () -> Content) {
    self.content = content()
  }

  func makeUIView(context: Context) -> UIScrollView {
    // set up the UIScrollView
    let scrollView = UIScrollView()
    scrollView.delegate = context.coordinator  // for viewForZooming(in:)
    scrollView.maximumZoomScale = 20
    scrollView.minimumZoomScale = 1
    scrollView.bouncesZoom = true

    // create a UIHostingController to hold our SwiftUI content
    let hostedView = context.coordinator.hostingController.view!
    hostedView.translatesAutoresizingMaskIntoConstraints = true
    hostedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    hostedView.frame = scrollView.bounds
    scrollView.addSubview(hostedView)

    return scrollView
  }

  func makeCoordinator() -> Coordinator {
    return Coordinator(hostingController: UIHostingController(rootView: self.content))
  }

  func updateUIView(_ uiView: UIScrollView, context: Context) {
    // update the hosting controller's SwiftUI content
    context.coordinator.hostingController.rootView = self.content
    assert(context.coordinator.hostingController.view.superview == uiView)
  }

  // MARK: - Coordinator

  class Coordinator: NSObject, UIScrollViewDelegate {
    var hostingController: UIHostingController<Content>

    init(hostingController: UIHostingController<Content>) {
      self.hostingController = hostingController
    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
      return hostingController.view
    }
  }
}

struct imagePicker:UIViewControllerRepresentable {
    @Binding var image: UIImage?
    @Binding var isShowingImagePicker: Bool
    
    typealias UIViewControllerType = UIImagePickerController
    typealias Coordinator = imagePickerCoordinator
    
    var sourceType:UIImagePickerController.SourceType = .camera
    
    func makeUIViewController(context: Context) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.sourceType = sourceType
        picker.delegate = context.coordinator
        return picker
    }
    
    func makeCoordinator() -> imagePickerCoordinator {
        return imagePickerCoordinator(image: $image, isShowingImagePicker: $isShowingImagePicker)
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
}

class imagePickerCoordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
    @Binding var image: UIImage?
    @Binding var isShowingImagePicker: Bool
    
    init(image:Binding<UIImage?>, isShowingImagePicker: Binding<Bool>) {
        _image = image
        _isShowingImagePicker = isShowingImagePicker
    }
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let uiimage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            image = uiimage
            isShowingImagePicker = false
        }
    }
    
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        isShowingImagePicker = false
    }
}

只想返回在圆圈中放大的图像。图像可以是方形的(re:300x300 帧),这很好,只需要缩放图像而不是整个屏幕或原始图像。

缩放的滚动视图图像

标签: swiftui

解决方案


根据评论,以下更改是成功的:

添加以下状态变量:

    @State private var rect: CGRect = .zero
    @State private var uiimage: UIImage? = nil  // resized image

选择图像后,将“RectGetter”添加到拾取的图像帧

                        if image != nil {
                            ZoomScrollView {
                                Image(uiImage: image!)
                                    .resizable()
                                    .scaledToFit()
                            }
                            .frame(width: 300, height: 300, alignment: .center)
                            .clipShape(Circle())
                            .background(RectGetter(rect: $rect))  

这是我添加的结构和扩展

extension UIView {
    func asImage(rect: CGRect) -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: rect)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }
}

struct RectGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { proxy in
            self.createView(proxy: proxy)
        }
    }

    func createView(proxy: GeometryProxy) -> some View {
        DispatchQueue.main.async {
            self.rect = proxy.frame(in: .global)
        }

        return Rectangle().fill(Color.clear)
    }
}

最后我将图像设置为保存

                        self.uiimage = UIApplication.shared.windows[0].rootViewController?.view.asImage(rect: self.rect)

这假定为根控制器。但是,在我的生产应用程序中,我必须指向 self

self.uiimage = UIApplication.shared.windows[0].self.asImage(rect: self.rect)

然后我能够保存该图像。

一些笔记。返回的图像是很好的矩形。然而,由于捕获图像的方式,圆形cropShape之外的其余矩形具有背景颜色。在这种情况下,圆圈外的角处为黄色。可能有一种方法可以让某种 ZOrder 蒙版在您调整图像大小时覆盖图像以进行显示,但随后会访问正确的图层并保存完整的矩形图片。如果有人想进一步提出建议,那将是一个更清洁的解决方案,但前提是您将始终以保存时相同的裁剪形状显示图片。


推荐阅读