2

I'm getting a low quality image when using ImageRenderer on iOS16 both on simulator and device. i.e. when saving the rendered image to the Photo Library or sending to Notes.. it's very pixelated.

Everything I read would suggest simply setting .scale but that appears to have no effect.

I'm including a sample project below and on GitHub. You can see the commented out sections which also fail.

It would seem that scale is being ignored completely.

Thanks in advance for your observations.

PS. The reason I'm providing a preview to the user is because in the preview I'm providing the user the ability to add some custom text which would not normally be in the regular UI. There'd be an update button to re-render to the preview.

import SwiftUI

struct helloWorldView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
        }
    }
}
struct ContentView: View {
    @State private var screenshotimage: UIImage?
    @State private var screenshot: Bool = false
    @State private var showsharesheet: Bool = false
    @State private var sharescreenshot: Bool = false
    @State private var imageToShare: Image?

    var body: some View {
        NavigationStack {
            helloWorldView()
            .padding()
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    Button("Share") {
                        showsharesheet.toggle()
                    }
                }
            }
            .sheet(isPresented: self.$showsharesheet) {
                NavigationStack {
                    ScrollView {
                        Section {
                            if screenshotimage != nil {
                                Image(uiImage: screenshotimage!)
                                ShareLink(
                                    item: Image(uiImage: screenshotimage!),
                                    preview: SharePreview(
                                        "Share Title",
                                        image: Image(uiImage: screenshotimage!)
                                    )
                                ) {
                                    Label("Share Image", systemImage: "square.and.arrow.up")
                                        .foregroundColor(.white)
                                        .padding()
                                        .background(.blue.gradient.shadow(.drop(radius: 1, x: 2, y: 2)), in: RoundedRectangle(cornerRadius: 5))
                                }
                            } else {
                                Text("Creating image..")
                            }
                        }
                    }
                    .toolbar {
                        ToolbarItem(placement: .cancellationAction) {
                            Button("Dismiss") {
                                showsharesheet = false
                            }
                        }
                    }
                    .navigationTitle("Preview")
                    .navigationBarTitleDisplayMode(.inline)
                }
                .onAppear() {
                    screenshot.toggle()
                }
                .onChange(of: screenshot, perform: { _ in
//                  Task {
                        let renderer =  ImageRenderer(content:helloWorldView())
//                      renderer.scale = UIScreen.main.scale
                        renderer.scale = 3.0
                        screenshotimage = renderer.uiImage
//                  }
                })
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
iOSDevil
  • 1,786
  • 3
  • 16
  • 29

1 Answers1

4

Sample Project

I was sharing the Image representation of that UIImage, which is a view representation and thus I ended up sharing the “screen sized image” rather than the full resolution of the underlying UIImage.

The updated sample project demonstrates how you can create your own Transferable image type.

import SwiftUI

struct helloWorldView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
        }
    }
}

    /// A struct that holds png image data.
struct PNG {
    private let data: Data
    
    init(_ data: Data) {
        self.data = data
    }
}

    // Transferable conformance, providing a DataRepresentation for ImageData.
@available(iOS 16.0, *)
extension PNG: Transferable {
    
    static var transferRepresentation: some TransferRepresentation {
        
        DataRepresentation<PNG>(contentType: .png) { imageData in
            imageData.data
        } importing: { data in
            PNG(data)
        }
    }
}

struct ContentView: View {
    @State private var screenshotimage: UIImage?
    @State private var screenshot: Bool = false
    @State private var showsharesheet: Bool = false
    @State private var sharescreenshot: Bool = false
    @State private var imageToShare: Image?
    
    var body: some View {
        NavigationStack {
            helloWorldView()
                .padding()
                .toolbar {
                    ToolbarItem(placement: .automatic) {
                        Button("Share") {
                            showsharesheet.toggle()
                        }
                    }
                }
                .sheet(isPresented: self.$showsharesheet) {
                    NavigationStack {
                        ScrollView {
                            Section {
                                if screenshotimage != nil {
                                    Image(uiImage: screenshotimage!)
                                    
                                    let photo = PNG((screenshotimage?.pngData())!) // create transferable 'image'
                                                                                   //                                   let photo: Photo = Photo(image: Image(uiImage: screenshotimage!), caption: "test") // first attempt, results in low quality
                                    
                                    ShareLink(
                                        item: photo,
                                        preview: SharePreview(
                                            "Share Title",
                                            image: photo
                                        )
                                    ) {
                                        Label("Share Image", systemImage: "square.and.arrow.up")
                                            .foregroundColor(.white)
                                            .padding()
                                            .background(.blue.gradient.shadow(.drop(radius: 1, x: 2, y: 2)), in: RoundedRectangle(cornerRadius: 5))
                                    }
                                } else {
                                    Text("Creating image..")
                                }
                            }
                        }
                        .toolbar {
                            ToolbarItem(placement: .automatic) {
                                Button("Dismiss") {
                                    showsharesheet = false
                                }
                            }
                        }
                        .navigationTitle("Preview")
                        .navigationBarTitleDisplayMode(.inline)
                    }
                    .presentationDetents([.medium, .large])
                    .onAppear() {
                        screenshot.toggle()
                    }
                    .onChange(of: screenshot, perform: { _ in
                        Task {
                            let renderer =  ImageRenderer(content:helloWorldView())
                            renderer.scale = UIScreen.main.scale
                            screenshotimage = renderer.uiImage
                        }
                    })
                }
        }
    }
}
iOSDevil
  • 1,786
  • 3
  • 16
  • 29
  • my understanding is that it's expected to post code here and not link out. but this solution did work for me. – lewis Dec 19 '22 at 11:05