ImageRenderer on iOS - generates blurry image, .scale ineffective?

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. You can see the commented out sections which also fail. It would seem that scale is being ignored completely.

Has anyone else experienced this?


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

Hello,

when saving the rendered image to the Photo Library or sending to Notes.. it's very pixelated.

This has something to do with the fact that you are sharing the transferable representation of an Image view of an image, rather than the rendered image data itself:

ShareLink(item: Image(uiImage: screenshotimage!)

Instead of sharing the transferable representation of the Image view, trying sharing the pngData() of the UIImage.

Running into the same issue on my end—

@gchiste When sharing the Data directly through ShareLink, it seems to share a binary file that doesn't automatically get interpreted as an image by the system.

Amusingly, I did find that taking the Data returned from .pngData() and passing it to a new UIImage, then passing that to an Image view and sharing through ShareLink does preserve the size 🎉 However it also seems to lose the alpha channel, so kind of a mixed bag.

let renderer =  ImageRenderer(content:helloWorldView())
renderer.scale = UIScreen.main.scale
renderer.scale = 3.0

if let image = renderer.uiImage,
    let data = image.pngData(),
    let fullImage = UIImage(data: data) {
          ShareLink(item: Image(uiImage: fullImage),
                    preview: SharePreview("", image: Image(uiImage: fullImage))) { /* etc */ }
}

Super weird....I had a similar problem.

  1. using ShareLink to send an image
  2. use ImageRenderer to generate image
  3. when converting from UIImage directly to Image the one that would "send" was fuzzy
  4. below is the code I used to fix it like the comment above.
@MainActor func render() {
    let renderer = ImageRenderer(content: viewToPost)
    renderer.scale = 3

    if let uiImage = renderer.uiImage {
      let data = uiImage.pngData()
      var fullImage = UIImage(data: data!)
      self.postImage = Image(uiImage: fullImage!)
      UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil)
    }
  }

for some reason if I don't convert into a new UIImage from the png data then the resulting image is fuzzy. Can someone with any insight comment? I like having a solution, but it keeps me up at night not UNDERSTANDING it.

Is it a bug of iOS 16.1.2? I noticed that the .scale seems to work well on iOS 16.2, but not on iOS 16.1.2.

ImageRenderer on iOS - generates blurry image, .scale ineffective?
 
 
Q