Press ESC to close

SwiftUI Camera & Gallery

Hello friends, in this article, we will look at topics such as how to take photos with SwiftUI and choose photos from the gallery. There is currently no control on SwiftUI side for these processes, which we use in almost many projects. That’s why we need to do this with UIKit. When we want to use an element from UIKit, we need to use the UIViewControllerRepresentable protocol.

First, let’s make a simple screen design. There will be a design with 2 buttons above. One of them is to take a photo from the camera and the other is to choose a photo from the gallery. Below these buttons, there will be a photo from the camera or gallery.

We define 3 different States on this screen. One of these states is whether the page is opened to take photos and select photos from the gallery. We keep it with the isImagePickerPresenting State. The other one is the last to keep the selected or taken photo, and it will be the option to take a photo from the camera or choose a photo from the gallery. Then we added a simple design.

//
//  ContentView.swift
//  swiftui_camera_gallery
//
//  Created by Omer Sezer on 27.12.2020.
//

import SwiftUI

struct ContentView: View {
    @State var isImagePickerPresenting: Bool = false
    @State var selectedPhoto: UIImage?
    @State var sourceType: UIImagePickerController.SourceType = .photoLibrary
    
    var body: some View {
        VStack {
            HStack {
                if UIImagePickerController.isSourceTypeAvailable(.camera) {
                    Button(action: {
                        sourceType = .camera
                        isImagePickerPresenting = true
                    }, label: {
                        Text("Camera")
                            .foregroundColor(.black)
                    })
                    Spacer()
                }
                
                Button(action: {
                    sourceType = .photoLibrary
                    isImagePickerPresenting = true
                }, label: {
                    Text("Gallery")
                        .foregroundColor(.black)
                })
            }
            .padding()
            Spacer()
            if let selectedPhoto = selectedPhoto {
                Image(uiImage: selectedPhoto)
                    .resizable()
            }
        }
        .sheet(isPresented: $isImagePickerPresenting, content: {
            ImagePicker(sourceType: sourceType, selectedPhoto: $selectedPhoto)
        })
    }
}

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

UIViewControllerRepresentable

The most important thing is to create a control by UIKit and communicate it with SwiftUI. I’m creating an ImagePicker control for this. This structure will take 2 variables. One of them is whether the photo or the gallery will open. The other is the variable created to transfer the captured or captured photo to SwiftUI. UIViewControllerRepresentable protocol requires 2 functions. Of these, makeUIViewController function, to create a control from UIKit. The other is to update that control. For me, it is enough to just create control here. Then I create a coordinator to connect its delegate. After taking or selecting a photo with this coordinator, I assign it to the selectedPhoto variable.

//
//  ImagePicker.swift
//  swiftui_camera_gallery
//
//  Created by Omer Sezer on 27.12.2020.
//

import UIKit
import SwiftUI

struct ImagePicker {
    var sourceType: UIImagePickerController.SourceType = .photoLibrary
    @Binding var selectedPhoto: UIImage?
}

extension ImagePicker: UIViewControllerRepresentable {
    
    func makeUIViewController(context: Context) -> some UIViewController {
        let imagePicker = UIImagePickerController()
        imagePicker.allowsEditing = true
        imagePicker.sourceType = sourceType
        imagePicker.delegate = context.coordinator
        return imagePicker
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        
    }
    
    func makeCoordinator() -> ImagePickerCoordinater {
        ImagePickerCoordinater(self)
    }
}

class ImagePickerCoordinater: NSObject  {
    var imagePicker: ImagePicker
    
    init(_ picker: ImagePicker) {
        self.imagePicker = picker
        super.init()
    }
}

extension ImagePickerCoordinater: UIImagePickerControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let selectedPhoto = info[.editedImage] as? UIImage ?? info[.originalImage] as? UIImage {
            imagePicker.selectedPhoto = selectedPhoto
        } else {
            imagePicker.selectedPhoto = nil
        }
        
        picker.dismiss(animated: true, completion: nil)
    }
}

extension ImagePickerCoordinater: UINavigationControllerDelegate { }

You can reach the project here. If you have questions, you can reach it by e-mail or comment. Good work.

Leave a Reply

Your email address will not be published. Required fields are marked *