I made this app which lets the user pick photos from their library and then export them as a pdf however only the first photo gets exported and then an alert pops up saying:
"Exported PNG image.png" couldn't be moved to "images" because either the former doesn't exist, or the folder containing the latter doesn't exist. The file doesn't exist.
I tried everything but couldn't get it to work. It's not even a file based app.
This is my code:
import SwiftUI
import UniformTypeIdentifiers
import PhotosUI
struct ImageDocument: FileDocument {
static var readableContentTypes: [UTType] {
[.image,.png,.jpeg]
}
var image = UIImage()
init(image: UIImage) {
self.image = image
}
init(configuration: ReadConfiguration) throws {
if let data = configuration.file.regularFileContents {
image = UIImage(data: data) ?? UIImage()
} else {
image = UIImage()
}
}
@MainActor func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
return FileWrapper(regularFileWithContents: Data(image.pngData()!))
}
}
struct ContentView: View {
@State private var selectedItems = [PhotosPickerItem]()
@State private var documents = [ImageDocument]()
@State private var showExporter = false
var body: some View {
NavigationStack {
ScrollView {
LazyVStack {
ForEach(0..<documents.count, id: \.self) { i in
Image(uiImage: documents[i].image)
.resizable()
.scaledToFit()
.frame(width: 300, height: 300)
}
}
}
.toolbar {
ToolbarItem {
PhotosPicker("Select images", selection: $selectedItems, matching: .images)
}
ToolbarItem {
Button("Export") {
showExporter.toggle()
}
}
}
.fileExporter(isPresented: $showExporter, documents: documents, contentType: .png, onCompletion: { result in
print(result)
})
.onChange(of: selectedItems) {
Task {
documents.removeAll()
for item in selectedItems {
if let image = try? await item.loadTransferable(type: Image.self) {
documents.append(ImageDocument(image: image.render() ?? UIImage()))
}
}
}
}
}
}
}
extension View {
/// Usually you would pass `@Environment(\.displayScale) var displayScale`
@MainActor func render(scale displayScale: CGFloat = 1.0) -> UIImage? {
let renderer = ImageRenderer(content: self)
renderer.scale = displayScale
return renderer.uiImage
}
}
}