YogiBear
YogiBear

Reputation: 51

Can't load camera using SwiftUI

I have 2 buttons, one to load an image from the gallery and one to load from the camera (i.e. take photo). But no matter what I try I keep getting gallery load instead of the camera. I've been using the following code (from my google searches) in my UIImagePickerController:

//
//  ImagePicker.swift
//

import Foundation
import SwiftUI

class ImagePickerCoordinator: NSObject, UINavigationControllerDelegate,             
UIImagePickerControllerDelegate {

@Binding var isShown: Bool
@Binding var image: Image?

init(isShown: Binding<Bool>, image: Binding<Image?>) {
    _isShown = isShown
    _image = image
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

    let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
    image = Image(uiImage: uiImage)
    isShown = false

}

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

}

struct ImagePicker: UIViewControllerRepresentable {

@Binding var isShown: Bool
@Binding var image: Image?

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

}

func makeCoordinator() -> ImagePickerCoordinator {
    return ImagePickerCoordinator(isShown: $isShown, image: $image)
}

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

}

struct CameraPicker: UIViewControllerRepresentable {

@Binding var isShown: Bool
@Binding var image: Image?

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

}

func makeCoordinator() -> ImagePickerCoordinator {
    return ImagePickerCoordinator(isShown: $isShown, image: $image)
}

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

}

struct CameraView: UIViewControllerRepresentable {

@Binding var isShown: Bool
@Binding var image: Image?

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

func makeUIViewController(context: UIViewControllerRepresentableContext<CameraView>) -> UIViewController {
    let cameraViewController = UIImagePickerController()
    cameraViewController.delegate = context.coordinator
    cameraViewController.sourceType = .camera
    cameraViewController.allowsEditing = false
    return cameraViewController
}

func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<CameraView>) {

}

class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    var parent: CameraView

    init(_ cameraView: CameraView) {
        self.parent = cameraView
    }

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

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

I basically duplicated the ImagePicker to cater for the camera (CameraPicker). Also tried Camera view from web searches when I couldn't get anywhere.

I'm new to swift so please let me know if there is a better way to do this.

To get image from camera:

//
//  PhotoCaptureView.swift


import SwiftUI

struct PhotoCaptureView: View {

    @Binding var showImagePicker: Bool
    @Binding var image: Image?

    var body: some View {
        CameraView(isShown: $showImagePicker, image: $image)
    }
}

#if DEBUG
struct PhotoCaptureView_Previews: PreviewProvider {
    static var previews: some View {
        PhotoCaptureView(showImagePicker: .constant(false), image: .constant(Image("")))
    }
}
#endif

To get image from gallery:

//
//  GalleryCapture.swift
//

import SwiftUI

struct GalleryCaptureView: View {

    @Binding var showImagePicker: Bool
    @Binding var image: Image?

    var body: some View {
        ImagePicker(isShown: $showImagePicker, image: $image)
    }
}

#if DEBUG
struct GalleryCaptureView_Previews: PreviewProvider {
    static var previews: some View {
        GalleryCaptureView(showImagePicker: .constant(false), image: .constant(Image("")))
    }
}
#endif

My contentview:

//
//  ContentView.swift
//

import SwiftUI

struct ContentView: View {

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

    // Function that converts an UIImage to Image
    func returnImage() -> Image
    {
        guard let img = image else {
            fatalError("Unable to load image")
        }
        return img
//        return Image(uiImage: img)
    }


    // Main Body
    var body: some View {
        NavigationView {
            VStack {
                VStack {
                    image?.resizable()
                        .frame(width: 299, height: 299)
                        .cornerRadius(6)
                }


//                returnImage()
//                    .resizable()
//                    .frame(width: 299, height: 299)
//                    .cornerRadius(6)
//                    .onTapGesture {
//                        self.showImagePicker = true
//                    }


                HStack {

                    Button(action: {self.showImagePicker.toggle()}) {
                        Image(systemName: "photo")
                            .font(.largeTitle)
                            .foregroundColor(.white)
                        Text("Gallery")
                                .foregroundColor(.white)
                            }.padding()
                            .background(Color.blue)
                            .cornerRadius(10)
                   .sheet(isPresented: self.$showImagePicker) {
                        GalleryCaptureView(showImagePicker: self.$showImagePicker, image: self.$image)
                   }

                    Button(action: {self.showImagePicker.toggle()}) {
                        Image(systemName: "camera")
                            .font(.largeTitle)
                            .foregroundColor(.white)
                        Text("Camera")
                        .foregroundColor(.white)
                    }.padding()
                    .background(Color.green)
                    .cornerRadius(10)
                    .sheet(isPresented: self.$showImagePicker) {
                        PhotoCaptureView(showImagePicker: self.$showImagePicker, image: self.$image)
                    }
                }
            }
            .navigationBarTitle("My first app")
        }
    }
}

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

I want to use MLkit so probably need to look at using UIImage instead of Image as well.

Upvotes: 1

Views: 1459

Answers (2)

YogiBear
YogiBear

Reputation: 51

Turned out to be so simple in the end. My mistake was to use the same boolean as the one I used for gallery.

Upvotes: 2

Dhaval Raval
Dhaval Raval

Reputation: 594

on button you have to self.showImagePicker to true like this :

 var body: some View {
        NavigationView{

            VStack {

                image?.resizable().scaledToFit()

                Button("Open Camera"){
                    self.showImagePicker = true
                }.padding()
                .foregroundColor(Color.white)
                .background(Color.purple)
                .cornerRadius(10)
            }.sheet(isPresented: self.$showImagePicker){
                PhotoCaptureView(showImagePicker: self.$showImagePicker, image: self.$image)
            }

            .navigationBarTitle(Text("Camera"))
        }
    }

inside the makeUIViewController method, you just need to add the following code right below picker.delegate = context.coordinator:

func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
    let picker = UIImagePickerController()
    picker.delegate = context.coordinator
    if !UIImagePickerController.isSourceTypeAvailable(.camera){
        picker.sourceType = .photoLibrary
    } else {
        picker.sourceType = .camera
    }
    return picker
}

Note : Camera opens only in real device you cant open in simulator.

OR

compare ur code with this awesome code , its working from my side

Upvotes: 1

Related Questions