-1

Using Swift 2.0 I am hoping to find a way to capture the resized image after the user has selected how they want to see it in the frame from the scroll view (ZoomScrollView).

I know there are complex examples out there from Swift but was hoping to find a simpler way to capture this in Swift 2.0. In all my searching I've heard references to using ZStack and some masks or overlays but can't find a simple good example.

I am hoping someone can update my example with the ZStack, masks, etc and how to extract the image for saving or provide a better example.

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

Just want to return the image that's zoomed in the circle. The image can be square (re: the 300x300 frame), that's fine just need the zoomed image not whole screen or the original image.

Zoomed scrollview image

David
  • 171
  • 2
  • 11
  • If you mean capture from screen, the next should be helpful https://stackoverflow.com/a/59333377/12299030. – Asperi Feb 10 '21 at 18:54
  • I don't want to capture the whole screen just the zoomed circular image I'm showing on the screen – David Feb 10 '21 at 23:52
  • The question sounds like a "how do I render the view to something else like a file"; if it is, then this looks like a reasonable starting point https://stackoverflow.com/questions/57200521/how-to-convert-a-view-not-uiview-to-an-image . Good luck. – shufflingb Feb 11 '21 at 10:24

1 Answers1

0

the following changes were successful based the comments:

Add the following State variables:

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

Added "RectGetter" to the picked image frame after image selected selected

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

Here is the struct and extension I added

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

Last I set the image to save

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

This assumes the root controller. However, in my production app I had to point to self

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

Then I was able to save that image.

A couple of notes. The image returned is the rectangle which is fine. However due to the way the image is captured the rest of the rectangle outside the cropShape of a circle has the background color. In this case yellow at the for corners outside the circle. There is probably a way to have some sort of ZOrder mask that overlays the image for display when you are resizing the image but then this accesses the right layer and saves the full rectangle picture. If anyone wants to suggest further that would be a cleaner solution but this works assuming you will always display the picture in the same crop shape it was saved in.

David
  • 171
  • 2
  • 11