I am trying to save certain views as png's inside my application (MacOS with SwiftUI). I have the methods below which take in some view, and then render the view and save it.
In my specific use case, whenever this function is called, the view is not being displayed on the current UI, thus I really shouldn't need this to run on the main thread, however it's required by the ImageRenderer.
Because of that, when I am using this to save images, my UI becomes unresponsive and frozen when trying to perform other actions. Is there any way I can avoid this and find a way to save the data for the view (creating the view as image) on a background thread?
func savePNG(image: NSImage, path: URL) {
let imageRepresentation = NSBitmapImageRep(data: image.tiffRepresentation!)
let pngData = imageRepresentation?.representation(using: .png, properties: [:])
do {
try pngData!.write(to: path)
} catch {
print(error)
}
}
@MainActor
func saveOne(dataone: some View, filePathIn: String, renderBump: CGFloat = 1.0) {
let render = ImageRenderer(content: dataone)
render.scale = renderBump
let new = render.nsImage
if let alpha = new {
let url = URL(filePath: "\(filePathIn).png")
savePNG(image: alpha, path: url)
}
}
Some more context: Basically there are API calls happening in the background. Based on the response, there is some view instantiated, and then sent to the image saver. This is happening frequently enough that even though it's quick rendering, it is briefly freezing the view many times which net effect is too much to ignore.
So if I have a super basic view like this
struct viewToSave: View {
let textIn: String
var body: some View {
VStack {
Text(textIn)
}.background(Color.black)
.frame(maxWidth: 950, maxHeight: 540)
}
}
And then some method in view model like this
@MainActor
func fetchDataAndMakeView() {
Task {
do {
let returned: someCodableType = try await network.getData()
let viewBasedOnReturned: viewToSave = viewToSave(textIn: returned.textProperty)
imgMain.shared.saveOne(dataone: viewBasedOnReturned, filePathIn: "somePath")
} catch {
print("error: \(error.localizedDescription)")
}
}
}
I feel like this would make no sense to have to block the main thread to perform my ultimate goal. Is there some other way of converting view to image in swift without doing on main thread with ImageRenderer?