1

I have work code like this, it let user pic image from gallery and drag in specific rectangle, the one problem, is save the green frame with picked and moved picture. I have function for screenshot, but I cant make it capture correct. It capture only frame without picked picture. It would be great, if some one can help me with figured this out

Image with green frame which I wont save Image which I have

import SwiftUI

struct picSelectorFrame:View {
    
    @State var isShowingImagePicker = false
    @State var      imageInBlackBox            = UIImage()
    @State var        imageSelected            = false
    @State var          imageZIndex            = 0.0
    @State private var currentPosition: CGSize = .zero
    @State private var     newPosition: CGSize = .zero
    
    var body: some View{
       let picSelector = ZStack{
             Image(uiImage: imageInBlackBox)
                 .resizable()
                 .scaledToFill()
                 .background(
                     Color.gray
                         .opacity(0.1)
                 )
                 .layoutPriority(10)
                 .zIndex(imageZIndex)
                 .gesture(DragGesture(minimumDistance: 5)
                             .onChanged({value in
                                 self.currentPosition = CGSize(width: value.translation.width + self.newPosition.width,
                                                               height: value.translation.height + self.newPosition.height)
                             })
                             .onEnded({ value in
                                 self.newPosition = self.currentPosition
                             }))
                 .offset(self.currentPosition)
             Button(action: {
                 if self.imageSelected == false {
                     self.isShowingImagePicker.toggle()
                     self.imageZIndex = 1
                     self.imageSelected = true
                 }
             }, label: {
                 Rectangle()
                     .opacity(0.1)
             }
             )
             .layoutPriority(12)
             .sheet(isPresented: $isShowingImagePicker , content: {
                 ImagePickerViewScreen(isPresented: self.$isShowingImagePicker,
                                       selectedImage: self.$imageInBlackBox)
             }
             
             )
         }
        
        ZStack{
         Rectangle()
             .fill( Color(.green))
             .opacity(0.3)
             .frame(width: 350, height: 350)
            picSelector
             .frame(width: 300, height: 300)
             .mask(
                 Rectangle()
             )
             .contentShape(
                 Rectangle()
             )
        }
        
    }
}
struct SwiftUIPhotoSelectorScreen: View {
let screenW = UIScreen.main.bounds.width
    let screenH = UIScreen.main.bounds.height
    var body: some View{
        ZStack{
                picSelectorFrame()
                Button("Save"){
                takeScreenshot(origin: CGPoint(x: screenW/2-175, y: screenH/2-175),size:CGSize(width: 350, height: 350))
                }
                .position(x: screenW/2, y: screenH-50)
        }
        .edgesIgnoringSafeArea(.all)
    }
}

struct ImagePickerViewScreen: UIViewControllerRepresentable {
    
    @Binding var isPresented: Bool
    @Binding var selectedImage: UIImage
    
    func makeUIViewController(context:
                                UIViewControllerRepresentableContext<ImagePickerViewScreen>) ->
    UIViewController {
        let controller = UIImagePickerController()
        controller.delegate = context.coordinator
        return controller
    }
    
    func makeCoordinator() -> ImagePickerViewScreen.Coordinator {
        return Coordinator(parent: self)
    }
    
    class Coordinator: NSObject, UIImagePickerControllerDelegate,
                       UINavigationControllerDelegate {
        
        let parent: ImagePickerViewScreen
        init(parent: ImagePickerViewScreen) {
            self.parent = parent
        }
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info:
                                    [UIImagePickerController.InfoKey : Any]) {
            if let selectedImageFromPicker = info[.originalImage] as? UIImage {
                self.parent.selectedImage = selectedImageFromPicker
            }
            self.parent.isPresented = false
        }
    }
    
    func updateUIViewController(_ uiViewController:
                                    ImagePickerViewScreen.UIViewControllerType, context:
                                        UIViewControllerRepresentableContext<ImagePickerViewScreen>) {
    }
}

extension View {
  func takeScreenshot(origin: CGPoint, size: CGSize) -> UIImage {
    let window = UIWindow(frame: CGRect(origin: origin, size: size))
    let hosting = UIHostingController(rootView: picSelectorFrame())
    window.rootViewController = hosting
    window.makeKeyAndVisible()
        let img = hosting.view.getImage()
        UIImageWriteToSavedPhotosAlbum(img, nil ,nil, nil)
    return img
  }
}

extension UIView {
    func getImage() -> UIImage {
        let format = UIGraphicsImageRendererFormat()
        let renderer = UIGraphicsImageRenderer(size: self.bounds.size, format: format)
        let image = renderer.image { rendererContext in
            self.layer.render(in: rendererContext.cgContext)
        }
        return image
    }
}

struct SwiftUIPhotoSelectorScreen_Previews: PreviewProvider {
    static var previews: some View {
        SwiftUIPhotoSelectorScreen()
    }
}
Davagaz
  • 854
  • 1
  • 10
  • 23
  • Does this answer your question https://stackoverflow.com/a/59333377/12299030? – Asperi Nov 11 '20 at 20:26
  • Does this answer your question? [How to convert a View (not UIView) to an image?](https://stackoverflow.com/questions/57200521/how-to-convert-a-view-not-uiview-to-an-image) – pawello2222 Nov 11 '20 at 22:42
  • should you done it 100% with screenshot? because there is other method as well – ios coder Nov 11 '20 at 23:30
  • Did you try using existing view when capturing instead of creating new one? Maybe replace ` let hosting = UIHostingController(rootView: picSelectorFrame())` with ` let hosting = UIHostingController(rootView: self)`. It seems that when you create a new view hierarchy, the image might be not picked there. – Denis Nov 12 '20 at 00:27
  • @Asperi Its first way that I try but it gave me only blank rectangles without image – Davagaz Nov 12 '20 at 12:32
  • @pawello2222 I tried this way too, but unfortunately, it gave me only blank rectangles as above – Davagaz Nov 12 '20 at 12:34
  • @Omid it doesn't have to be exactly the screenshot method. The only reason is that the user moves the picture, which makes it difficult to build the view programmatically – Davagaz Nov 12 '20 at 12:46
  • @Denis Yes, this is exactly the way I'm looking but replacing to rootView: self, still give me just rectangles without picture. – Davagaz Nov 12 '20 at 12:51

1 Answers1

0

I got a similar problem - I get an image from the Photo library and then try to render it using view.layer.render(in: context), however, no image appears in the output.

I fixed my issue by making sure the image that comes from the UIImagePickerController is represented in 32 bits per pixel format (8 bits per component):

    func normalizeImage(_ image: UIImage) -> UIImage? {    
        guard let cgImage = image.cgImage else {
            return nil
        }

        let width = cgImage.width
        let height = cgImage.height
        let data = UnsafeMutablePointer<UInt32>.allocate(capacity: width * height)
        defer {
            data.deallocate()
        }
        var bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Big.rawValue
        bitmapInfo |= CGImageAlphaInfo.premultipliedLast.rawValue & CGBitmapInfo.alphaInfoMask.rawValue
            
        guard let context = CGContext(
            data: data,
            width: width,
            height: height,
            bitsPerComponent: 8,
            bytesPerRow: width * 4,
            space: CGColorSpaceCreateDeviceRGB(),
            bitmapInfo: bitmapInfo
        ) else {
            return nil
        }
        context.draw(cgImage, in: .init(origin: .zero, size: .init(width: width, height: height)))

        guard let output = context.makeImage() else {
            return nil
        }
        return UIImage(cgImage: output)
    }    

I would call that function in the UIImagePickerControllerDelegate to process the incoming .originalImage.

Vladimir Grigorov
  • 10,903
  • 8
  • 60
  • 70