Skip to content

Commit

Permalink
Using PHPicker (#425)
Browse files Browse the repository at this point in the history
  • Loading branch information
etoledom authored Sep 26, 2024
1 parent 27632aa commit a2879ce
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import PhotosUI
import SwiftUI

struct CameraImagePicker: UIViewControllerRepresentable {
let onImageSelected: (ImagePickerItem) -> Void

@State private var pickedImage: ImagePickerItem? {
didSet {
if let pickedImage {
onImageSelected(pickedImage)
}
}
}

func makeUIViewController(context: Context) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
// Forcefully use UIImagePickerController for device camera only
imagePicker.sourceType = .camera
imagePicker.delegate = context.coordinator
// Use custom image cropper
imagePicker.allowsEditing = false

return imagePicker
}

func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
// No-op
}

func makeCoordinator() -> Coordinator {
Coordinator(self)
}

final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: CameraImagePicker

init(_ parent: CameraImagePicker) {
self.parent = parent
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
parent.pickedImage = .init(id: UUID().uuidString, image: image)
}
}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import PhotosUI
import SwiftUI

struct PhotosImagePicker: UIViewControllerRepresentable {
let onImageSelected: (ImagePickerItem) -> Void
let onCancel: () -> Void

@State private var pickedImage: ImagePickerItem? {
didSet {
if let pickedImage {
onImageSelected(pickedImage)
}
}
}

func makeUIViewController(context: Context) -> PHPickerViewController {
var config = PHPickerConfiguration()
config.filter = .images
config.selectionLimit = 1
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}

func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
// No-op
}

func makeCoordinator() -> Coordinator {
Coordinator(self)
}

final class Coordinator: NSObject, PHPickerViewControllerDelegate {
let parent: PhotosImagePicker

init(_ parent: PhotosImagePicker) {
self.parent = parent
}

func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
guard
let result = results.first,
result.itemProvider.canLoadObject(ofClass: UIImage.self)
else {
parent.onCancel()
return
}

Task {
let image = await result.itemProvider.loadUIImage()
let imageItem = ImagePickerItem(id: UUID().uuidString, image: image)
parent.pickedImage = imageItem
}
}
}
}

extension NSItemProvider {
fileprivate func loadUIImage() async -> UIImage {
await withCheckedContinuation { continuation in
loadObject(ofClass: UIImage.self) { itemReading, _ in
guard let image = itemReading as? UIImage else {
return
}

continuation.resume(returning: image)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ struct SystemImagePickerView<Label, ImageEditor: ImageEditorView>: View where La
let onImageSelected: (UIImage) -> Void

var body: some View {
// NOTE: Here we can choose between legacy and new picker.
// So far, the new SwiftUI PhotosPicker only supports Photos library, no camera, and no cropping, so we are only using legacy for now.
// The interface (using a Label property as the element to open the picker) is the same as in the new SwiftUI picker,
// which will make it easy to change it later on.
ImagePicker(label: label, onImageSelected: onImageSelected, customEditor: customEditor)
}
}
Expand Down Expand Up @@ -79,13 +75,15 @@ private struct ImagePicker<Label, ImageEditor: ImageEditorView>: View where Labe
case .camera:
ZStack {
Color.black.ignoresSafeArea(edges: .all)
LegacyImagePickerRepresentable(sourceType: source.map()) { item in
CameraImagePicker { item in
pickerDidSelectImage(item)
}
}
case .photoLibrary:
LegacyImagePickerRepresentable(sourceType: source.map()) { item in
PhotosImagePicker { item in
pickerDidSelectImage(item)
} onCancel: {
sourceType = nil
}.ignoresSafeArea()
}
}
Expand Down Expand Up @@ -130,60 +128,7 @@ extension ImagePicker.SourceType {
}
}

struct LegacyImagePickerRepresentable: UIViewControllerRepresentable {
@Environment(\.presentationMode) private var presentationMode

var sourceType: UIImagePickerController.SourceType
let onImageSelected: (ImagePickerItem) -> Void

@State private var pickedImage: ImagePickerItem? {
didSet {
if let pickedImage {
onImageSelected(pickedImage)
}
}
}

func makeUIViewController(context: UIViewControllerRepresentableContext<LegacyImagePickerRepresentable>) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = sourceType
imagePicker.delegate = context.coordinator

return imagePicker
}

func updateUIViewController(
_ uiViewController: UIImagePickerController,
context: UIViewControllerRepresentableContext<LegacyImagePickerRepresentable>
) {}

func makeCoordinator() -> Coordinator {
Coordinator(self)
}

final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: LegacyImagePickerRepresentable

init(_ parent: LegacyImagePickerRepresentable) {
self.parent = parent
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
guard let url = info[UIImagePickerController.InfoKey.imageURL] as? URL else { return }
if picker.allowsEditing, let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
parent.pickedImage = .init(image: image, url: url)
} else if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
parent.pickedImage = .init(image: image, url: url)
}
}
}
}

struct ImagePickerItem: Identifiable {
var id: String {
url.absoluteString
}

struct ImagePickerItem: Identifiable, Sendable {
let id: String
let image: UIImage
let url: URL
}

0 comments on commit a2879ce

Please sign in to comment.