Swift

SwiftUI에서 UIImagePickerController 사용

JDeoks 2023. 5. 1. 00:54

카메라로 찍거나, 앨범에서 고른 사진을 변수에 저장하는 기능을 구현하려고 한다.

UIKit을 같이 사용해서 구현했다.

 

import SwiftUI
import UIKit

struct ContentView: View {
    /// 이미지 피커를 보여줄지 여부를 결정하는 State
    @State private var showingImagePicker = false
    /// 선택한 이미지를 저장하는 State
    @State private var inputImage: UIImage?

    var body: some View {
        VStack {
            if let inputImage = inputImage {
                // 선택한 이미지가 있으면 화면에 표시
                Image(uiImage: inputImage)
                    .resizable()
                    .scaledToFit()
            }

            // 이미지 피커를 표시하는 버튼
            Button("사진 선택") {
                showingImagePicker = true
            }
        }
        // .sheet를 .fullScreenCover로 변경
        // present 여부를 $showingImagePicker로 결정함
        // .sheet나 .fullScreenCover를 사용하면, 해당 뷰를 닫을 때 자동으로 isPresented와 연결된 변수 false로 설정
        .fullScreenCover(isPresented: $showingImagePicker, onDismiss: loadImage) {
            // 이미지 피커를 표시
            ImagePicker(image: $inputImage)
        }
    }

    /// 이미지가 선택되고, ImagePicker가 dismiss되면 실행되는 함수
    func loadImage() {
        // 이미지를 저장하거나 처리하려면 여기에서 수행
    }
}
//
struct ImagePicker: UIViewControllerRepresentable {
    /// 뷰의 presentation 상태에 접근하는 데 사용된다. 뷰를 닫는 동작을 처리하기 위해 사용
    @Environment(\.presentationMode) var presentationMode
    /// 선택한 이미지를 저장하는  변수. 다른 뷰와 값이 동기화되어야 하므로 @Binding이 사용됨
    @Binding var image: UIImage?

    // UIViewControllerRepresentable에 정의되어 있음
    // UIViewController 객체가 생성됨과 동시에 호출, Coordinator 객체를 생성
    func makeCoordinator() -> Coordinator {
        // init 메서드에 자신(ImagePicker)넣음
        Coordinator(self)
    }

    // UIViewControllerRepresentable을 채택한 뷰가 생성될 때 호출
    // UIImagePickerController를 생성하고 반환
    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        // delegate를
        picker.delegate = context.coordinator
        // picker.sourceType = .camera
        picker.sourceType = .photoLibrary // 앨범에서 이미지를 선택하도록 설정
        return picker
    }

    // 이미지 피커를 업데이트하는 함수 (본 예제에서는 필요하지 않음)
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {}

    // UIImagePickerControllerDelegate와 UINavigationControllerDelegate를 구현하는 Coordinator 클래스
    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        let parent: ImagePicker

        init(_ parent: ImagePicker) {
            self.parent = parent
        }

        // 이미지가 선택되면 호출되는 함수
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            if let uiImage = info[.originalImage] as? UIImage {
                parent.image = uiImage
            }
            parent.presentationMode.wrappedValue.dismiss()
        }
    }

}

ImagePicker 구조체는 swiftUI에서 UIViewController를 사용하기 위해 UIViewControllerRepresentable 프로토콜을 채택했다.

이로써, SwiftUI 뷰 내에서 UIKit 기반 뷰 컨트롤러를 사용할 수 있으며, SwiftUI와 UIKit 간의 상호 운용성을 확보할 수 있다.

 

해당 프로토콜을 구현하기 위해서는 아래의 메서드를 구현해야한다.

makeUIViewController(context: Context) -> UIViewControllerType: 

SwiftUI에서 사용할 UIViewController 인스턴스를 생성하고 초기 설정을 수행
updateUIViewController(_ uiViewController: UIViewControllerType, context: Context): 

SwiftUI 뷰가 업데이트되면 호출되어, UIViewController 인스턴스를 업데이트하는 데 사용

 

대략적인 플로우를 설명하자면 이렇다. 

버튼을 누르면 showingImagePicker값이 바뀌어 ImagePicker가 생성된다.

UIViewControllerRepresentable을 채택한 뷰인 ImagePicker가 생성될 때 makeUIViewController(context:)함수가 호출된다.

이 함수는 뷰의 응답 대상인 UIViewController 객체를 생성하고 반환한다.  

UIViewController 객체가 생성됨과 동시에,  makeCoordinator()함수가 호출되는데, 이 함수는 해당 뷰와 함께 동작하는 Coordinator 객체를 생성한다. 이때 파라미터로 self(ImagePicker)를 넣어서 parent에 저장한다.

이미지가 선택되면 imagePickerController(_: didFinishPickingMediaWithInfo:)가 호출되고 parent.image에 선택된 이미지를 저장한 후 parent를 dismiss한다.

dismiss하게되면 fullScreenCover의 isPresented와 연결된 변수는 자동으로 false로 설정된다.

 

 

https://enebin.medium.com/swiftui%EB%A7%8C-%EC%8D%A8%EC%84%9C-%ED%98%B8%EB%8B%A4%EB%8B%A5-%EC%B9%B4%EB%A9%94%EB%9D%BC%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-intro-65fd95ba5327

 

SwiftUI만 써서 호다닥 카메라앱 만들기 feat.MVVM [Intro]

이 시리즈에서 할 일은~ 최대한 SwiftUI만 사용하여 기성품과 다름 없는 카메라 앱을 만드는 것이다.

enebin.medium.com

나중에 볼 링크