0

I have tried more than a few ways to convert a view to an image in swift. The one that seems the simplest is Hacking With Swift's implementation. The problem is, it returns a blank image and a not in the console saying "[Snapshotting] View (view) drawing with afterScreenUpdates:YES inside CoreAnimation commit is not supported.

I have also tried getting an image from the current graphics context to no avail. I have put things in DispatchQueue.main.async, no matter what I do, I still don't get an image of the view.

Here is my code:

The View

struct ShareView: View {
    
    @Binding var weightEntries: [WeightEntry]
    @ObservedObject var goal: Goal
    var settings: Settings
    @State var showingShareSheet = false
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        ...
            .sheet(isPresented: $showingShareSheet) {
                ShareSheetView(activityItems: [ShareProgressView(weightEntries: weightEntries, goal: goal, settings: settings).snapshot()])
            }
    }
}

The View Extension

extension View {
    func snapshot() -> UIImage {
        let controller = UIHostingController(rootView: self)
        let view = controller.view

        let targetSize = controller.view.intrinsicContentSize
        view?.bounds = CGRect(origin: .zero, size: targetSize)
        view?.backgroundColor = .clear

        let renderer = UIGraphicsImageRenderer(size: targetSize)

        return renderer.image { _ in
            view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
        }
    }
}

The Share Sheet View

struct ShareSheetView: UIViewControllerRepresentable {
    typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
    let activityItems: [Any]
    let applicationActivities: [UIActivity]? = nil
    let excludedActivityTypes: [UIActivity.ActivityType]? = nil
    let callback: Callback? = nil
    func makeUIViewController(context: Context) -> UIActivityViewController {
        let controller = UIActivityViewController(
            activityItems: activityItems,
            applicationActivities: applicationActivities)
        controller.excludedActivityTypes = excludedActivityTypes
        controller.completionWithItemsHandler = callback
        return controller
    }
    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
        // nothing to do here
    }
}

Thank you so much for your help, I really appreciate this especially since I have been stuck on this for a long time now. Have a nice day :)

Mark Reggiardo
  • 467
  • 2
  • 4
  • 15
  • Does this answer your question https://stackoverflow.com/a/59333377/12299030? – Asperi May 25 '22 at 07:30
  • @Asperi, thanks for the reply. Unfortunately, I get a SIGABRT at `layer.render(in:)`. – Mark Reggiardo May 25 '22 at 08:04
  • Just retested with Xcode 13.4 / iOS 15.5 - still works (updated with small tuning to correct bounds). It works and in Preview and in Simulator/Device. Probably it is something specific in your code. – Asperi May 25 '22 at 08:17

0 Answers0