My test app grabs a photo from the device, and then displays it. Simple.
On the simulator, it works great, but on a device, it rotates the image. Sometimes 90 degrees, sometimes 180, sometimes it's not rotated.
Here is the code I am using, minus the button that initiates PhotoPicker:
struct ContentView: View {
@State private var theImage: Image = Image(systemName : "chart.bar.doc.horizontal")
@State private var selectedPhoto: PhotosPickerItem? = nil
var body: some View {
VStack {
theImage
.resizable()
.scaledToFit()
.foregroundColor(.white)
PhotosPicker(selection: $selectedPhoto, matching: .images) {
GetPhotoButton()
}
.task(id: selectedPhoto, {
await loadImage(from: selectedPhoto)
selectedPhoto = nil
})
}
.padding()
}
private func loadImage(from item: PhotosPickerItem?) async {
if let theItem = item {
do {
let image = try await theItem.loadTransferable(type: Image.self)
if let image = image {
theImage = image
}
} catch {
print("Failed to load image: \(error)")
}
}
}
}
Thanks to @outlaw for pointing me in the right direction.
In Apple's documentation for PhotosPickerItem
, they state:
Important:
Image
only supportsPNG
file types through itsTransferable
conformance ...
So it appears that by using loadTransferable(type: Image.self)
we are losing important information about the asset, namely its orientation
.
So here is a custom Transferable
SwiftUI protocol (thanks to Itsuki) to cover formats other than PNG:
struct TransferableImage: Transferable {
let image: Image
enum TransferError: Error {
case importFailed
}
static var transferRepresentation: some TransferRepresentation {
DataRepresentation(importedContentType: .image) { data in
guard let uiImage = UIImage(data: data) else {
throw TransferError.importFailed
}
let image = Image(uiImage: uiImage)
return TransferableImage(image: image)
}
}
}
And replace the .task(id:
with an .onChange(of:
PhotosPicker(selection: $selectedPhoto, matching: .images) {
GetPhotoButton()
}
.onChange(of: selectedPhoto) {
Task {
if let loaded = try? await selectedPhoto?.loadTransferable(type: TransferableImage.self) {
theImage = loaded.image
} else {
print("Failed to load image")
}
}
}
And voila, it works for all images!