Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 241 Vote(s) - 3.65 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to open the ImagePicker in SwiftUI?

#1
I need to open an image picker in my app using SwiftUI, how can I do that?

I thought about using the `UIImagePickerController`, but I don't know how to do that in SwiftUI.
Reply

#2
We can use [**PhotosPicker**][1] since **iOS** 16.0, **iPadOS** 16.0, **macOS** 13.0, **watchOS** 9.0 and above.

Simple example:

import PhotosUI
import SwiftUI

struct MediaView: View {
@State private var photosPickerPresented = false
@State private var photoPickerItems = [PhotosPickerItem]()

var body: some View {
Button {
// Present photo Picker
photosPickerPresented.toggle()
} label: {
Text("Show Photos Picker")
}
.photosPicker(isPresented: $photosPickerPresented, selection: $photoPickerItems)
}
}


[1]:

[To see links please register here]

Reply

#3
Cleaned up version for Xcode 12 available via SPM as Swift Package:

[To see links please register here]


Source:

```swift
import SwiftUI

public struct ImagePickerView: UIViewControllerRepresentable {

private let sourceType: UIImagePickerController.SourceType
private let onImagePicked: (UIImage) -> Void
@Environment(\.presentationMode) private var presentationMode

public init(sourceType: UIImagePickerController.SourceType, onImagePicked: @escaping (UIImage) -> Void) {
self.sourceType = sourceType
self.onImagePicked = onImagePicked
}

public func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = self.sourceType
picker.delegate = context.coordinator
return picker
}

public func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}

public func makeCoordinator() -> Coordinator {
Coordinator(
onDismiss: { self.presentationMode.wrappedValue.dismiss() },
onImagePicked: self.onImagePicked
)
}

final public class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

private let onDismiss: () -> Void
private let onImagePicked: (UIImage) -> Void

init(onDismiss: @escaping () -> Void, onImagePicked: @escaping (UIImage) -> Void) {
self.onDismiss = onDismiss
self.onImagePicked = onImagePicked
}

public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let image = info[.originalImage] as? UIImage {
self.onImagePicked(image)
}
self.onDismiss()
}

public func imagePickerControllerDidCancel(_: UIImagePickerController) {
self.onDismiss()
}

}

}
```
Reply

#4
iOS 14 Xcode 12 - Photo Picker SwiftUI with Reusable View with limits allowed

struct ImagePickerView: UIViewControllerRepresentable {

@Binding var images: [UIImage]
@Binding var showPicker: Bool
var selectionLimit: Int

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

func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { }

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

class Coordinator: NSObject, PHPickerViewControllerDelegate {

var parent: ImagePickerView

init(parent: ImagePickerView) {
self.parent = parent
}

func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {

parent.showPicker.toggle()

for img in results {
if img.itemProvider.canLoadObject(ofClass: UIImage.self) {
img.itemProvider.loadObject(ofClass: UIImage.self) { (image, err) in
guard let image1 = image else { return }

DispatchQueue.main.async {
self.parent.images.append(image1 as! UIImage)
}
}
} else {
// Handle Error
parent.showPicker.toggle()
}
}
}
}
}

then in View you can do

VStack {
Image(systemName: "camera.viewfinder")
.resizable()
.aspectRatio(contentMode: .fit)
.onTapGesture {
self.viewModel.pickerBool.toggle()
}
}
.sheet(isPresented: self.$viewModel.pickerBool) {
ImagePickerView(images: self.$viewModel.images, showPicker: self.$viewModel.pickerBool, selectionLimit: 3)
}
Reply

#5
You need to wrap `UIImagePickerController` in a struct implementing `UIViewControllerRepresentable`.

For more about `UIViewControllerRepresentable`, please check this amazing WWDC 2019 talk:

[Integrating SwiftUI][1]


```swift
struct ImagePicker: UIViewControllerRepresentable {

@Environment(\.presentationMode)
private var presentationMode

let sourceType: UIImagePickerController.SourceType
let onImagePicked: (UIImage) -> Void

final class Coordinator: NSObject,
UINavigationControllerDelegate,
UIImagePickerControllerDelegate {

@Binding
private var presentationMode: PresentationMode
private let sourceType: UIImagePickerController.SourceType
private let onImagePicked: (UIImage) -> Void

init(presentationMode: Binding<PresentationMode>,
sourceType: UIImagePickerController.SourceType,
onImagePicked: @escaping (UIImage) -> Void) {
_presentationMode = presentationMode
self.sourceType = sourceType
self.onImagePicked = onImagePicked
}

func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
onImagePicked(uiImage)
presentationMode.dismiss()

}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
presentationMode.dismiss()
}

}

func makeCoordinator() -> Coordinator {
return Coordinator(presentationMode: presentationMode,
sourceType: sourceType,
onImagePicked: onImagePicked)
}

func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = sourceType
picker.delegate = context.coordinator
return picker
}

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

}

}
```

Here's a simple view to test it:

- The picker is displayed in a sheet
- the selected image appears without any sort of animation, and replaces the `Show image picker` button

```swift
struct ContentView: View {

@State var showImagePicker: Bool = false
@State var image: Image? = nil

var body: some View {
ZStack {
VStack {
Button(action: {
self.showImagePicker.toggle()
}) {
Text("Show image picker")
}
image?.resizable().frame(width: 100, height: 100)
}
.sheet(isPresented: $showImagePicker) {
ImagePicker(sourceType: .photoLibrary) { image in
self.image = Image(uiImage: image)
}
}
}
}
}
```

I hope this helps as a starting point!

I'm sure Apple will make this easier to do once `SwiftUI` is out of beta.

[![enter image description here][2]][2]


[1]:

[To see links please register here]

[2]:


**Tested on Xcode 11.4**

**Bugs:**

- @JAHelia found a bug on the picker when `sourceType` is not the camera.
You won't be able to drag down the sheet - I haven't been able to find a solution yet.
Reply

#6
I implemented it like this:



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)
}

}


Add the logic of just two buttons to your View that's enough...))
Reply

#7
I implemented a version that I think is more general and extensible. I used a `Subject` instead of a `Binding` to solve the problem where it's undoable/inappropriate to add another Binding in your view.

**For example, you created a `List` showing a set of images stored in the underlying storage and you wanted to add an image with the image picker. In this case, it's very hard/ugly to have that image added to your underlying storage.**

So I used a subject to transfer the image and you can simply observe it and add the new images to some storage, or if you want it to behave just like a Binding, it's one line of code, too. (modifying your State in your observation)

Then I wrapped the preferences into a `ViewModel` so it won't get cluttered if you want to have more subjects or configurations.

```
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>()
}
```

Usage:
```
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
}
}
}
}
```
Reply

#8
Based on @user:2890168 I made a version that:

- retrieves `UIImage` instead of `Image`
- use `.sheet` to present the `ImagePicker`.
- shows `ActionSheet` to help users to remove or change the image.

[![enter image description here][1]][1]

struct LibraryImage: View {

@State var showAction: Bool = false
@State var showImagePicker: Bool = false

@State var uiImage: UIImage? = nil

var sheet: ActionSheet {
ActionSheet(
title: Text("Action"),
message: Text("Quotemark"),
buttons: [
.default(Text("Change"), action: {
self.showAction = false
self.showImagePicker = true
}),
.cancel(Text("Close"), action: {
self.showAction = false
}),
.destructive(Text("Remove"), action: {
self.showAction = false
self.uiImage = nil
})
])

}


var body: some View {
VStack {

if (uiImage == nil) {
Image(systemName: "camera.on.rectangle")
.accentColor(Color.App.purple)
.background(
Color.App.gray
.frame(width: 100, height: 100)
.cornerRadius(6))
.onTapGesture {
self.showImagePicker = true
}
} else {
Image(uiImage: uiImage!)
.resizable()
.frame(width: 100, height: 100)
.cornerRadius(6)
.onTapGesture {
self.showAction = true
}
}

}

.sheet(isPresented: $showImagePicker, onDismiss: {
self.showImagePicker = false
}, content: {
ImagePicker(isShown: self.$showImagePicker, uiImage: self.$uiImage)
})

.actionSheet(isPresented: $showAction) {
sheet
}
}
}


The default `body` of `LibraryImage` is an `Image` that shows a camera icon that is tappable by the users.

On tap event, the image picker is shown with a `sheet` modifier. After the image selection, the `LibraryImage` body is recomputed and now shows the `Image` defined in else statement (because `uiImage` property now contains the image picked by the user).

Now, on tap event the `ActionSheet` is shown.

The edited image picker:

struct ImagePicker: UIViewControllerRepresentable {

@Binding var isShown: Bool
@Binding var uiImage: UIImage?

class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

@Binding var isShown: Bool
@Binding var uiImage: UIImage?

init(isShown: Binding<Bool>, uiImage: Binding<UIImage?>) {
_isShown = isShown
_uiImage = uiImage
}

func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let imagePicked = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
uiImage = imagePicked
isShown = false
}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
isShown = false
}

}

func makeCoordinator() -> Coordinator {
return Coordinator(isShown: $isShown, uiImage: $uiImage)
}

func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
return picker
}

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

}

}

default behaviour:

[![enter image description here][2]][2]


[1]:

[2]:
Reply

#9
Here's a version that works in Xcode 11 beta 4.

It uses a BindableObject singleton (ImagePicker.shared) with two properties: .view and .image.

See usage below (ImagePickerTestView)

```
import SwiftUI
import Combine

final class ImagePicker : BindableObject {

static let shared : ImagePicker = ImagePicker()

private init() {} //force using the singleton: ImagePicker.shared

let view = ImagePicker.View()
let coordinator = ImagePicker.Coordinator()

// Bindable Object part
let willChange = PassthroughSubject<Image?, Never>()

@Published var image: Image? = nil {
didSet {
if image != nil {
willChange.send(image)
}
}
}
}


extension ImagePicker {

class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

// UIImagePickerControllerDelegate
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
ImagePicker.shared.image = Image(uiImage: uiImage)
picker.dismiss(animated:true)
}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated:true)
}
}


struct View: UIViewControllerRepresentable {

func makeCoordinator() -> Coordinator {
ImagePicker.shared.coordinator
}

func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker.View>) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
return picker
}

func updateUIViewController(_ uiViewController: UIImagePickerController,
context: UIViewControllerRepresentableContext<ImagePicker.View>) {

}

}

}


struct ImagePickerTestView: View {

@State var showingPicker = false

@State var image : Image? = nil
// you could use ImagePicker.shared.image directly

var body: some View {
VStack {
Button("Show image picker") {
self.showingPicker = true
}

image?
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 300)

}.sheet(isPresented: $showingPicker,
onDismiss: {
// do whatever you need here
}, content: {
ImagePicker.shared.view
})
.onReceive(ImagePicker.shared.$image) { image in
// This gets called when the image is picked.
// sheet/onDismiss gets called when the picker completely leaves the screen
self.image = image
}
}

}

#if DEBUG
struct ImagePicker_Previews : PreviewProvider {

static var previews: some View {
ImagePickerTestView()
}
}
#endif
Reply

#10
I'm very new at Swift, but I was able to get it with the following.

This will load up an image picker modal and let you select a photo, and it will then update an `@State` variable from a parent.

If this works for you, you can replace the `@State` with something that can span across multiple components, such as `@EnvironmentObject` so other components can get updated as well.

Hope this helps!

```
// 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)
}
}
}
```

Usage:
```
// SampleView.swift

struct SampleView : View {
var body: some View {
PresentationLink(destination: ImagePicker().environmentObject(self.userData), label: {
Text("Import Photo")
})
}
}
```

Once again, I am fresh into Swift so if anyone has some comments, please let me know! Happy to learn more.
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through