Reputation: 25
I'm working on a SwiftUI project using PHPickerViewController. Everything currently works fine except that whenever I open the picker, will get the warning [Picker] Showing picker unavailable UI (reason: still loading) with error: (null)
Seems like the picker is not fully initialized when it gets called, and indeed it shows Loading Photos...
when launch.
Does anyone know how to solve this?
My code is like follows:
...
.toolbar {
ToolbarItem {
Button(action: {
showingImagePicker = true
}) {
Label("Add Image", systemImage: "plus")
}
}
}
.sheet(isPresented: $showingImagePicker) {
ImagePickerRepresentable(
showImagePicker: $showingImagePicker,
onCompletion: complete
)
}
this is my ImagePickerRepresentable
struct ImagePickerRepresentable: UIViewControllerRepresentable {
// MARK: - Environment Object
@Binding var showImagePicker: Bool
let onCompletion: (UIImage) -> Void
// MARK: - Coordinator Class
final class Coordinator: PHPickerViewControllerDelegate {
private let parent: ImagePickerRepresentable
init(_ parent: ImagePickerRepresentable) {
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
parent.showImagePicker = false
// unpack the selected items
let itemProviders = results.map(\.itemProvider)
for itemProvider in itemProviders {
if itemProvider.canLoadObject(ofClass: UIImage.self) {
itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
if let image = image as? UIImage {
self?.parent.onCompletion(image)
}
}
} else {
print("Can't load object")
}
}
}
}
func makeUIViewController(context: Context) -> PHPickerViewController {
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
configuration.filter = .images // filter only to images
configuration.selectionLimit = 3
let controller = PHPickerViewController(configuration: configuration)
controller.delegate = context.coordinator // Use Coordinator for delegation
return controller
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) { }
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
Upvotes: 0
Views: 984
Reputation: 484
I tested your code and I get the same error, but it still works.
You can use my version of the picker if you want to (metadata variable is just for Firebase):
import Foundation
import SwiftUI
import PhotosUI
struct PHPicker: UIViewControllerRepresentable {
// var didFinishPicking: (_ didSelectItems: Bool) -> Void
@Binding var images: [UIImage]
@Binding var picker: Bool
@Binding var imageData: [Data]
var selectionLimitation: Int {
return 5 - images.count
}
func makeCoordinator() -> Coordinator {
return PHPicker.Coordinator(with: self)
}
func makeUIViewController(context: Context) -> PHPickerViewController {
var config = PHPickerConfiguration()
// can also select videos
config.filter = .images
config.selectionLimit = selectionLimitation
config.preferredAssetRepresentationMode = .current
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
}
class Coordinator: NSObject, PHPickerViewControllerDelegate {
var photoPicker: PHPicker
init(with photoPicker: PHPicker) {
self.photoPicker = photoPicker
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
// photoPicker.didFinishPicking(!results.isEmpty)
// Closing picker
photoPicker.picker.toggle()
for img in results {
if img.itemProvider.canLoadObject(ofClass: UIImage.self) {
img.itemProvider.loadObject(ofClass: UIImage.self) { object, err in
if let image = object as? UIImage {
if let mediaData = image.jpegData(compressionQuality: 0.5) {
DispatchQueue.main.async {
self.photoPicker.images.append(image)
self.photoPicker.imageData.append(mediaData)
}
}
}
}
} else {
// Error loading
print("Cannot be loaded")
}
}
}
}
}
Tested with:
import SwiftUI
struct ContentView: View {
@State var showingImagePicker = false
@State var images: [UIImage] = []
@State var imagesData: [Data] = []
var body: some View {
NavigationView {
VStack {
if !images.isEmpty {
ForEach(images, id:\.self) { img in
Image(uiImage: img)
.resizable()
.scaledToFit()
}
}
}
.toolbar {
ToolbarItem {
Button(action: {
showingImagePicker = true
}) {
Label("Add Image", systemImage: "plus")
}
}
}
.sheet(isPresented: $showingImagePicker) {
// ImagePickerRepresentable(
// showImagePicker: $showingImagePicker,
// onCompletion: complete
// )
PHPicker(images: $images, picker: $showingImagePicker, imageData: $imagesData)
}
}
}
}
Upvotes: 1