SwiftUI Can't Print Rendered Image to Printer from ShareLink (iOS 16+)

508 views Asked by At

I'm trying to add the ability for a user to print a graph to a networked printer. I use ImageRenderer to create an Image. The ShareLink does raise a share sheet which includes a preview of the image but tapping on the print button does nothing. No printer dialog, no error, no console log.

I must be missing something important in the ShareLink setup. In the simplified code below, I display the image once rendered so I know that piece works. I can also copy the image from the share sheet and paste it into a message.

A screenshot of the app after tapping the Render button:

enter image description here

A screenshot of the share sheet after the Export button is tapped:

enter image description here

The behavior is the same with the simulator and a real device. And yes, I can print to the printer from other apps.

The code:

struct LinkView: View {
    @Environment(\.displayScale) var displayScale
    @State private var renderedImage = Image(systemName: "photo")
    @State private var showActivityControllerView: Bool = false

    var mainView: some View {
        VStack {
            Text("This is the Graph to capture")
            Chart(Thing.exampleThings) { thing in
                BarMark(
                    x: .value("Name", thing.name),
                    y: .value("Generation", thing.generation)
                )
            }
            .frame(height: 250)
        }//v
    }//var main

    var body: some View {

        VStack {
            ShareLink("Export", item: renderedImage, preview: SharePreview(Text("Shared Image"), image: renderedImage))
        
            mainView
                .padding()
        
            renderedImage
                .resizable()
                .frame(height: 250)
                .padding()
            Button {
                render()
            } label: {
                Label("Render", systemImage: "photo.circle")
                    .font(.title)
            }
        }//v
    }//body

    @MainActor
    func render() {
        let renderer = ImageRenderer(content: mainView)
        renderer.scale = displayScale
    
        if let uiImage = renderer.uiImage {
            renderedImage = Image(uiImage: uiImage)
        }
    }//render
}//struct capture

struct Thing: Identifiable {

    let id = UUID()
    let name: String
    let generation: Double

    static var exampleThings = [
    
        Thing(name: "One", generation: 10.0),
        Thing(name: "Two", generation: 20.0),
        Thing(name: "Three", generation: 30.0),
        Thing(name: "Four", generation: 25.0),
        Thing(name: "Five", generation: 15.0),
        Thing(name: "Six", generation: 5.0)
    ]

}//thing

Any guidance would be appreciated. Xcode 14.3, iOS 16.2

1

There are 1 answers

2
Ian Hocking On BEST ANSWER

I've had some trouble with this, and my workaround is to make sure that the ShareLink gets a URL (to the PNG, for instance). This then will lead to the print sheet successfully appearing.

I get the URL (for my custom view) like this:

  /// Provides an export URL for a PNG of the thought group.
  ///
  /// - Parameter filename: The filename (without extension) to use.
  public func exportPngUrl(filename: String) throws -> URL {
        let url = URL.documentsDirectory.appending(path: "\(filename).png")

        let img = self.thoughtGroupView.imageMap()

        guard let data = img.pngData() else {
            throw SCPThoughtGroupExportError.couldNotGetPngData("PNG data could not be generated from the document")
        }

        do {
            try data.write(to: url, options: .atomic)
            return url
        }
    }

And then I use the simplest initialiser for the ShareLink:

ShareLink("Share", item: exportPngUrl(filename: document.filenameWithoutExt))