SwiftUIを使用してアプリでImagePickerを開く必要があります。どうすればよいですか?
UIImagePickerControllerを使用することを考えましたが、SwiftUIでそれを行う方法がわかりません。
より一般的で拡張可能なバージョンを実装しました。私はSubject
ではなくBinding
を使用して、ビューに別のバインディングを追加することが元に戻せない/不適切であるという問題を解決しました。
たとえば、基盤となるストレージに保存されている一連の画像を表示するList
を作成し、画像ピッカーを使用して画像を追加したい場合。この場合、基になるストレージに画像が追加されました。
したがって、私はサブジェクトを使用して画像を転送し、それを観察して新しい画像をストレージに追加するか、またはバインディングのように動作させたい場合は、コードも1行です。 (観察での状態の変更)
次に、設定をViewModel
にラップしました。これにより、より多くのサブジェクトまたは構成が必要な場合でも、混乱しないようになっています。
import SwiftUI
import Combine
struct ImagePickerView : UIViewControllerRepresentable {
@Binding var model: ImagePickerViewModel
typealias UIViewControllerType = UIImagePickerController
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<Self>) -> UIImagePickerController {
let controller = UIImagePickerController()
controller.delegate = context.coordinator
controller.allowsEditing = false
controller.mediaTypes = ["public.image"]
controller.sourceType = .photoLibrary
return controller
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePickerView>) {
// run right after making
}
class Coordinator : NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parentView: ImagePickerView
init(_ parentView: ImagePickerView) {
self.parentView = parentView
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
parentView.model.isPresented = false
}
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
guard let uiImage = info[.originalImage] as? UIImage else { return }
let image = Image(uiImage: uiImage)
parentView.model.pickedImagesSubject?.send([image])
parentView.model.isPresented = false
}
}
}
struct ImagePickerViewModel {
var isPresented: Bool = false
let pickedImagesSubject: PassthroughSubject<[Image], Never>! = PassthroughSubject<[Image], Never>()
}
使用法:
struct SomeView : View {
@EnvironmentObject var storage: Storage
@State var imagePickerViewModel = ImagePickerViewModel()
var body: some View {
Button(action: { self.imagePickerViewModel.isPresented.toggle() }) { ... }
.sheet(isPresented: $imagePickerViewModel.isPresented) {
ImagePickerView(model: self.$imagePickerViewModel)
}
.onReceive(imagePickerViewModel.pickedImagesSubject) { (images: [Image]) -> Void in
withAnimation {
// modify your storage here
self.storage.images += images
}
}
}
}
私はそれを次のように実装しました:
import SwiftUI
final class ImagePickerCoordinator: NSObject {
@Binding var image: UIImage?
@Binding var takePhoto: Bool
init(image: Binding<UIImage?>, takePhoto: Binding<Bool>) {
_image = image
_takePhoto = takePhoto
}
}
struct ShowImagePicker: UIViewControllerRepresentable {
@Binding var image: UIImage?
@Binding var takePhoto: Bool
func makeCoordinator() -> ImagePickerCoordinator {
ImagePickerCoordinator(image: $image, takePhoto: $takePhoto)
}
func makeUIViewController(context: Context) -> UIImagePickerController {
let pickerController = UIImagePickerController()
pickerController.delegate = context.coordinator
guard UIImagePickerController.isSourceTypeAvailable(.camera) else { return pickerController }
switch self.takePhoto {
case true:
pickerController.sourceType = .camera
case false:
pickerController.sourceType = .photoLibrary
}
pickerController.allowsEditing = true
return pickerController
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
}
extension ImagePickerCoordinator: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let uiImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
self.image = uiImage
picker.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
たった2つのボタンのロジックをビューに追加すれば十分です...))
私はSwiftで非常に新しいですが、次のようにしてそれを得ることができました。
これにより、イメージピッカーモーダルが読み込まれ、写真を選択できます。次に、親から@State
変数を更新します。
これで問題が解決した場合は、@State
を@EnvironmentObject
などの複数のコンポーネントにまたがることができるものに置き換えて、他のコンポーネントも更新できるようにすることができます。
お役に立てれば!
// ImagePicker.Swift
struct ImagePicker : View {
@State var image: UIImage? = nil
var body: some View {
ImagePickerViewController(image: $image)
}
}
// ImagePickerViewController.Swift
import UIKit
import AVFoundation
import SwiftUI
struct ImagePickerViewController: UIViewControllerRepresentable {
@Binding var image: UIImage?
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePickerViewController>) {
}
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePickerViewController>) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = UIImagePickerController.SourceType.photoLibrary
imagePicker.allowsEditing = false
imagePicker.delegate = context.coordinator
return imagePicker
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate, AVCapturePhotoCaptureDelegate {
var parent: ImagePickerViewController
init(_ parent: ImagePickerViewController) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let imagePicked = info[.originalImage] as! UIImage
parent.image = imagePicked
picker.dismiss(animated: true, completion: nil)
}
}
}
使用法:
// SampleView.Swift
struct SampleView : View {
var body: some View {
PresentationLink(destination: ImagePicker().environmentObject(self.userData), label: {
Text("Import Photo")
})
}
}
繰り返しになりますが、Swiftに慣れているので、コメントがありましたらお知らせください!詳細をお知らせください。