How to Open the Imagepicker in Swiftui

How to open the ImagePicker in SwiftUI?

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

https://github.com/ralfebert/ImagePickerView

Source:

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

}

}

ImagePicker in SwiftUI

ok, looks like you are testing with 13.1 -> update to 13.2 - there it works

How to pick image from gallery in SwiftUI

I am using presentationMode here, to check for the view that is it presenting or not?

By using this you can dismiss() the UIImagePickerController.

Sample code for SwiftUI preview:

import SwiftUI

struct ContentView: View {

@State var isShowPicker: Bool = false
@State var image: Image? = Image("placeholder")

var body: some View {
NavigationView {
ZStack {
VStack {
image?
.resizable()
.scaledToFit()
.frame(height: 320)
Button(action: {
withAnimation {
self.isShowPicker.toggle()
}
}) {
Image(systemName: "photo")
.font(.headline)
Text("IMPORT").font(.headline)
}.foregroundColor(.black)
Spacer()
}
}
.sheet(isPresented: $isShowPicker) {
ImagePicker(image: self.$image)
}
.navigationBarTitle("Pick Image")
}
}
}

struct ImagePicker: UIViewControllerRepresentable {

@Environment(\.presentationMode)
var presentationMode

@Binding var image: Image?

class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

@Binding var presentationMode: PresentationMode
@Binding var image: Image?

init(presentationMode: Binding<PresentationMode>, image: Binding<Image?>) {
_presentationMode = presentationMode
_image = image
}

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

}

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

}

func makeCoordinator() -> Coordinator {
return Coordinator(presentationMode: presentationMode, image: $image)
}

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

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

}

}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ContentView()
}
}
}

How to get the image from image picker and display into a add form?

You can use a Binding to pass data from a child view back up to a parent:

struct FileView: View {
@Binding var fileUrl : URL?

var body: some View {
Button("Select File") {
let openPanel = NSOpenPanel()
openPanel.prompt = "Select File"
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = false
openPanel.canCreateDirectories = false
openPanel.canChooseFiles = true
openPanel.allowedFileTypes = ["png","jpg","jpeg"]
openPanel.begin { (result) -> Void in
if result.rawValue == NSApplication.ModalResponse.OK.rawValue {
fileUrl = openPanel.url
}
}
}
}
}

struct ContentView: View {
@State var fileUrl: URL?

var body: some View {
VStack {
FileView(fileUrl: $fileUrl)
if let fileUrl = fileUrl, let image = NSImage(contentsOf: fileUrl) {
Image(nsImage: image)
}
}
}
}

Notice that in the child, it's @Binding, but in the parent, it's @State.

Updated version, using Data:

struct FileView: View {
@Binding var fileData : Data?

var body: some View {
Button("Select File") {
let openPanel = NSOpenPanel()
openPanel.prompt = "Select File"
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = false
openPanel.canCreateDirectories = false
openPanel.canChooseFiles = true
openPanel.allowedFileTypes = ["png","jpg","jpeg"]
openPanel.begin { (result) -> Void in
if result.rawValue == NSApplication.ModalResponse.OK.rawValue {
guard let url = openPanel.url, let data = try? Data(contentsOf: url) else {
//handle errors here
return
}
fileData = data
}
}
}
}
}

struct ContentView: View {
@State var fileData: Data?

var body: some View {
VStack {
FileView(fileData: $fileData)
if let fileData = fileData, let image = NSImage(data: fileData) {
Image(nsImage: image)
}
}
}
}

Note: obviously, not doing any error handling for not being able to read from the URL or anything in this example

SwiftUI MacOSX ImagePicker

initialize the panel like this:

let myFiledialog = NSOpenPanel()
myFiledialog.prompt = “Select path”

to add a selectbox like you want you need to add this:

myFiledialog.worksWhenModal = true
myFiledialog.canChooseDirectories = false
myFiledialog.canChooseFiles = true

you can select the datatype of your choice with

myFiledialog.allowedFileTypes = [“png”, “jpg”, “jpeg”]

to disable the multiselect add this.

myFiledialog.allowsMultipleSelection = false

i hope this helped you.

How do I make two separate calls to an Imagepicker in SwiftUI?

The root cause of the problem is using sheet(isPresented:) which can be problematic on iOS 14 because it loads the sheet content of the first render of it (in your case, the ImagePicker for the first image) and then doesn't update as one might expect (see SwiftUI @State and .sheet() ios13 vs ios14 for example).

The solution is to use sheet(item:). In your case, this also involved some other refactoring to get things to work as expected. Here's what I came up with:

struct ContentView: View {
@State private var inputImage1: UIImage?
@State private var inputImage2: UIImage?
@State private var activeImagePicker : ImagePickerIdentifier? = nil

enum ImagePickerIdentifier : String, Identifiable {
case picker1, picker2

var id : String {
return self.rawValue
}
}

var image1 : Image {
if let inputImage1 = inputImage1 {
return Image(uiImage: inputImage1)
} else {
return Image("Placeholder")
}
}

var image2 : Image {
if let inputImage2 = inputImage2 {
return Image(uiImage: inputImage2)
} else {
return Image("Placeholder")
}
}

var body: some View {
VStack{
Form {
Section(header: Text("First Image")){
image1
.resizable()
.aspectRatio(contentMode: .fit)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 200, alignment: .center)
.clipShape(Rectangle())
.onTapGesture { self.activeImagePicker = .picker1 }
}
Section(header: Text("Second Image")) {
image2
.resizable()
.aspectRatio(contentMode: .fit)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 200, alignment: .center)
.clipShape(Rectangle())
.onTapGesture { self.activeImagePicker = .picker2 }
}

}
.sheet(item: $activeImagePicker) { picker in
switch picker {
case .picker1:
ImagePicker(image: $inputImage1)
case .picker2:
ImagePicker(image: $inputImage2)
}
}
}
}
}

Note that I got rid of your onDismiss function, because it was unnecessary here, but if you did need it for your real implementation, I'd suggest using onDisappear on the individual ImagePickers, which will let you differentiate which one is being dismissed.



Related Topics



Leave a reply



Submit