diff --git a/Projects/Core/DesignSystem/Sources/CameraPicker/CameraPicker.swift b/Projects/Core/DesignSystem/Sources/CameraPicker/CameraPicker.swift new file mode 100644 index 00000000..ded69478 --- /dev/null +++ b/Projects/Core/DesignSystem/Sources/CameraPicker/CameraPicker.swift @@ -0,0 +1,92 @@ +import SwiftUI +import Photos +import UIKit + +public extension View { + func cameraPicker( + isShow: Binding, + pickedImageResult: Binding + ) -> some View { + self.fullScreenCover(isPresented: isShow) { + CameraPicker(pickedImageResult: pickedImageResult, isPresented: isShow) + } + } +} + +struct CameraPicker: UIViewControllerRepresentable { + var sourceType: UIImagePickerController.SourceType = .camera + @Binding var pickedImageResult: PickedImageResult? + @Binding var isPresented: Bool + + init(pickedImageResult: Binding, isPresented: Binding) { + self._pickedImageResult = pickedImageResult + self._isPresented = isPresented + } + + func makeCoordinator() -> ImagePickerViewCoordinator { + return ImagePickerViewCoordinator(pickedImageResult: $pickedImageResult, isPresented: $isPresented) + } + + func makeUIViewController(context: Context) -> UIImagePickerController { + let pickerController = UIImagePickerController() + pickerController.sourceType = .camera + pickerController.delegate = context.coordinator + pickerController.allowsEditing = true + pickerController.mediaTypes = ["public.movie", "public.image"] + pickerController.cameraFlashMode = .auto + return pickerController + } + + func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {} +} + +final class ImagePickerViewCoordinator: + NSObject, + UINavigationControllerDelegate, + UIImagePickerControllerDelegate { + + @Binding var pickedImageResult: PickedImageResult? + @Binding var isPresented: Bool + + init(pickedImageResult: Binding, isPresented: Binding) { + self._pickedImageResult = pickedImageResult + self._isPresented = isPresented + } + + func imagePickerController( + _ picker: UIImagePickerController, + didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any] + ) { + let filename: String + if let asset = info[.phAsset] as? PHAsset, let fileName = asset.value(forKey: "filename") as? String { + filename = fileName + } else if let url = info[.imageURL] as? URL { + filename = url.lastPathComponent + } else if let url = info[.mediaURL] as? URL { + filename = url.lastPathComponent + } else { + filename = "" + } + + if let image = info[.editedImage] as? UIImage { + let pickedImageResult = PickedImageResult(fileName: filename, uiImage: image) + self.pickedImageResult = pickedImageResult + } else if let image = info[.originalImage] as? UIImage { + let pickedImageResult = PickedImageResult(fileName: filename, uiImage: image) + self.pickedImageResult = pickedImageResult + } else if let mediaURL = info[.mediaURL] as? URL { + Task(priority: .userInitiated) { + let (data, _) = try await URLSession.shared.data(from: mediaURL) + guard let image = UIImage(data: data) else { return } + let pickedImageResult = PickedImageResult(fileName: filename, uiImage: image) + self.pickedImageResult = pickedImageResult + } + } + + self.isPresented = false + } + + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + self.isPresented = false + } +} diff --git a/Projects/Core/DesignSystem/Sources/ImagePicker/MiltipleImagePicker.swift b/Projects/Core/DesignSystem/Sources/ImagePicker/MiltipleImagePicker.swift new file mode 100644 index 00000000..8e2ee251 --- /dev/null +++ b/Projects/Core/DesignSystem/Sources/ImagePicker/MiltipleImagePicker.swift @@ -0,0 +1,64 @@ +import SwiftUI +import PhotosUI + +public extension View { + func imagePicker( + isShow: Binding, + pickedImageResults: Binding<[PickedImageResult]>, + filter: PHPickerFilter = .images, + limit: Int = 1 + ) -> some View { + self.fullScreenCover(isPresented: isShow) { + MultipleImagePicker(filter: filter, limit: limit, pickedImageResults: pickedImageResults) + } + } +} + +struct MultipleImagePicker: UIViewControllerRepresentable { + let filter: PHPickerFilter + let limit: Int + @Binding var pickedImageResults: [PickedImageResult] + + func makeUIViewController(context: Context) -> PHPickerViewController { + var configuration = PHPickerConfiguration(photoLibrary: .shared()) + configuration.filter = filter + configuration.selectionLimit = limit + let controller = PHPickerViewController(configuration: configuration) + controller.delegate = context.coordinator + return controller + } + + func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {} + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + class Coordinator: PHPickerViewControllerDelegate { + var parent: MultipleImagePicker + + init(_ parent: MultipleImagePicker) { + self.parent = parent + } + + func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { + for result in results { + let provider = result.itemProvider + provider.loadFileRepresentation(forTypeIdentifier: "public.item") { [weak self] url, _ in + guard provider.canLoadObject(ofClass: UIImage.self) else { return } + provider.loadObject(ofClass: UIImage.self) { image, _ in + guard let image = image as? UIImage else { return } + DispatchQueue.main.async { + let pickedImageResult = PickedImageResult( + fileName: url?.lastPathComponent ?? "", + uiImage: image + ) + self?.parent.pickedImageResults.append(pickedImageResult) + } + } + } + } + picker.dismiss(animated: true) + } + } +} diff --git a/Projects/Core/DesignSystem/Sources/ImagePicker/PickedImage.swift b/Projects/Core/DesignSystem/Sources/ImagePicker/PickedImage.swift new file mode 100644 index 00000000..a47931dd --- /dev/null +++ b/Projects/Core/DesignSystem/Sources/ImagePicker/PickedImage.swift @@ -0,0 +1,7 @@ +import Foundation +import UIKit + +public struct PickedImageResult: Equatable { + public let fileName: String + public let uiImage: UIImage +} diff --git a/Projects/Core/DesignSystem/Sources/ImagePicker/SingleImagePicker.swift b/Projects/Core/DesignSystem/Sources/ImagePicker/SingleImagePicker.swift new file mode 100644 index 00000000..19952cc7 --- /dev/null +++ b/Projects/Core/DesignSystem/Sources/ImagePicker/SingleImagePicker.swift @@ -0,0 +1,62 @@ +import SwiftUI +import PhotosUI + +public extension View { + func imagePicker( + isShow: Binding, + pickedImageResult: Binding, + filter: PHPickerFilter = .images + ) -> some View { + self.fullScreenCover(isPresented: isShow) { + SingleImagePicker(filter: filter, pickedImageResult: pickedImageResult) + } + } +} + +struct SingleImagePicker: UIViewControllerRepresentable { + let filter: PHPickerFilter + @Binding var pickedImageResult: PickedImageResult? + + func makeUIViewController(context: Context) -> PHPickerViewController { + var configuration = PHPickerConfiguration(photoLibrary: .shared()) + configuration.filter = filter + configuration.selectionLimit = 1 + let controller = PHPickerViewController(configuration: configuration) + controller.delegate = context.coordinator + return controller + } + + func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {} + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + class Coordinator: PHPickerViewControllerDelegate { + var parent: SingleImagePicker + + init(_ parent: SingleImagePicker) { + self.parent = parent + } + + func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { + for result in results { + let provider = result.itemProvider + provider.loadFileRepresentation(forTypeIdentifier: "public.item") { [weak self] url, _ in + guard provider.canLoadObject(ofClass: UIImage.self) else { return } + provider.loadObject(ofClass: UIImage.self) { image, _ in + guard let image = image as? UIImage else { return } + DispatchQueue.main.async { + let pickedImageResult = PickedImageResult( + fileName: url?.lastPathComponent ?? "", + uiImage: image + ) + self?.parent.pickedImageResult = pickedImageResult + } + } + } + } + picker.dismiss(animated: true) + } + } +}