How to share a subview in a ShareSheet in SwiftUI

Viewed 250

I am trying to share an image of a view through the Share Sheet. This is what i am doing.

One defines an extension to UIView and View where one utilizes the UIGraphicsImageRenderer to output an UIImage and modifies the .asImage to output the picture as an UIImage.

It seems to me the simplest approach to making it work. But the image is not showing in the ShareSheet.

import SwiftUI

struct SnapshottingAPI: View {
    @State private var isShareSheetShowing = false
    let imageSize: CGSize = CGSize(width: 1000, height: 1000)
    
    var body: some View {
        VStack {
            
            ZStack {
                Color.blue
                Circle()
                    .fill(Color.red)
            }
            .frame(width: 300, height: 300)
            
            Text("text")
            Button(action: shareButton){
                Image(systemName: "square.and.arrow.up")
                    .frame(width: 35, height: 35)
            }
        }
    }
    
    func shareButton() {
        isShareSheetShowing.toggle()
        let highresImage = self.asImage(size: imageSize)
        let outputimage = Image(uiImage: highresImage)
        let items: [Any] = [outputimage]
        
        let av = UIActivityViewController(activityItems: items, applicationActivities: nil)
        UIApplication.shared.windows.first?.rootViewController!.present(av, animated: true, completion: nil)
    }
}


extension UIView {
    func asImage() -> UIImage {
        let format = UIGraphicsImageRendererFormat()
        format.scale = 1
        return UIGraphicsImageRenderer(size: self.layer.frame.size, format: format).image { context in
            self.drawHierarchy(in: self.layer.bounds, afterScreenUpdates: true)
        }
    }
}


extension View {
    func asImage(size: CGSize) -> UIImage {
        let controller = UIHostingController(rootView: self)
        controller.view.bounds = CGRect(origin: .zero, size: size)
        let image = controller.view.asImage()
        return image
    }
}
struct SnapshottingAPI_Previews: PreviewProvider {
    static var previews: some View {
        SnapshottingAPI()
    }
}

View

1 Answers

I found a way to do it using some other posts. This is simple enough for me. Albeit its missing the thumbnail image in the ShareSheet

import SwiftUI
import LinkPresentation

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

struct SO1: View {
    @State private var rect1: CGRect = .zero
    @State private var uiimage: UIImage? = nil
    @State private var isShareSheetShowing = false

    var body: some View {
        VStack {
            HStack {
                VStack {
                    Text("LEFT")
                    Text("VIEW")
                }
                .padding(20)
                .background(Color.green)
                .border(Color.blue, width: 5)
                .background(RectGetter(rect: $rect1))
                
                Button(action: shareButton){
                    Image(systemName: "square.and.arrow.up")
                        .frame(width: 35, height: 35)
                }

            }

        }

    }
    
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return ""
    }

    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
        return nil
    }

    func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
        let image = UIImage(named: "YourImage")!
        let imageProvider = NSItemProvider(object: image)
        let metadata = LPLinkMetadata()
        metadata.imageProvider = imageProvider
        return metadata
    }
    
    func shareButton() {
        isShareSheetShowing.toggle()
        self.uiimage = UIApplication.shared.windows[0].rootViewController?.view.asImage(rect: self.rect1)
        //let outputimage = Image(uiImage: self.uiimage!)
        let items: [Any] = [self.uiimage!, self, "Test Text"]
        let av = UIActivityViewController(activityItems: items, applicationActivities: nil)
        UIApplication.shared.windows.first?.rootViewController!.present(av, animated: true, completion: nil)
    }
}

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

struct SO1_Previews: PreviewProvider {
    static var previews: some View {
        SO1()
    }
}
Related