Starbucks Caramel Frappuccino
본문 바로가기
  • 그래 그렇게 조금씩
SwiftUI/SwiftUI(Advanced)

11. UIViewControllerRepresentable / UIImagePicker

by Toughie 2023. 6. 26.

🦁UIViewControllerRepresentable / UIImagePicker🦁

지난 시간에 공부한 UIViewRepresentable이 UIView를 SwiftUI 뷰로 통합할 수 있게 하는 프로토콜이라면,

UIViewControllerRepresentable은 UIViewController를 SwiftUI 뷰로 통합하기 위한 프로토콜이다.

뷰 컨트롤러를 생성하고, 뷰 컨트롤러를 업데이트하는 메서드를 구현해야한다.

 

https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable

 

UIViewControllerRepresentable | Apple Developer Documentation

A view that represents a UIKit view controller.

developer.apple.com

SwiftUI에서 UIKit 기반의 UIViewController를 SwiftUI 뷰로 사용 할 수 있게 해주는 프로토콜.

SwiftUI에서 사용 가능한 뷰로 간주되고, UIViewController를 포함하고 있기 때문에 UIKit에서 제공하는 기능을 사용할 수 있다.


간단한 활용 예시

UIViewController 서브클래스 생성

class FirstViewController: UIViewController {
    
    var labelText: String = "Default Value"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        //UIViewController를 만들 때 기본적으로 빈 UIView가 함께 생성됨.
        /*
        override func loadView() {
            super.loadView()
            view = UIView()
        }
        */
        view.backgroundColor = .blue
        
        let label = UILabel()
        label.text = labelText
        label.textColor = UIColor.white
        
        //label을 뷰 컨트롤러의 뷰에 추가함.
        view.addSubview(label)
        //label이 뷰컨 전체 뷰를 채우도록
        label.frame = view.frame
        
    }
}

UIViewControllerRepresentable 채택한 구조체 생성

struct BasicUiViewControllerRepresentable: UIViewControllerRepresentable {
    
    let labelText: String
    
    func makeUIViewController(context: Context) -> some UIViewController {
        let vc = FirstViewController()
        vc.labelText = labelText
        return vc
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        
    }
}

UIImagePickerController

이미지 피커를 활용하는 방법을 알아보자.

https://developer.apple.com/documentation/uikit/uiimagepickercontroller/

 

UIImagePickerController | Apple Developer Documentation

A view controller that manages the system interfaces for taking pictures, recording movies, and choosing items from the user’s media library.

developer.apple.com

유저의 미디어 라이프러리에서 사진,영상을 선택해서 활용할 때 사용할 수 있다.


UIImagePickerControllerRepresentable

struct UIImagePickerControllerRepresentable: UIViewControllerRepresentable {
    //전달할 이미지
    @Binding var image: UIImage?
    //sheet를 통해 피커를 띄우는 구조에서 사진을 선택하면 sheet가 dismiss되도록 하기 위해 
    //showScreen 바인딩 처리
    @Binding var showScreen: Bool
    
    //context는 UIViewControllerRepresentable의 instance
    func makeUIViewController(context: Context) -> UIImagePickerController {
        let vc = UIImagePickerController()
        //이미지 편집 기능 활성화 여부
        vc.allowsEditing = false
        //뷰컨의 delegate를 coordinator로 설정
        vc.delegate = context.coordinator
        return vc
    }
    
    //SwiftUI to UIKit
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
        
    }
    
    //UIKit to SwiftUI
    //이미지 피커 뷰컨의 delegate가 될 Coordinator 초기화 메서드
    func makeCoordinator() -> Coordinator {
        return Coordinator(image: $image, showScreen: $showScreen)
    }
    
    //Coordinator 클래스
    // 총 3가지 프로토콜을 채택해야 한다.
    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        
        @Binding var image: UIImage?
        @Binding var showScreen: Bool
        
        init(image: Binding<UIImage?>, showScreen: Binding<Bool>) {
            self._image = image
            self._showScreen = showScreen
        }
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            guard let newImage = info[.originalImage] as? UIImage else { return }
            image = newImage
            showScreen = false
        }
    }
}

UIImagePickerControllerDelegate 프로토콜을 구현하는 델리게이트 메서드.

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            guard let newImage = info[.originalImage] as? UIImage else { return }
            image = newImage
            showScreen = false
        }

이미지 피커에서 유저가 미디어(이미지/비디오)를 선택한 후 호출됨.

info는 [UIImagePickerController.InfoKey : Any] 딕셔너리 형태로 되어 있음.

guard문을 통해 딕셔너리에서 .originalImage 키에 해당하는 값, 즉 선택한 이미지를 가져옴 -> UIImage로 타입캐스팅.

바인딩 되어 있는 image에 선택한 이미지를 할당 (이미지 전달)

showScreen을 false로 설정해서 피커뷰를 닫음


View

struct UIViewControllerRepresentablePrac: View {
    
    @State private var showScreen: Bool = false
    @State private var image: UIImage?
    
    var body: some View {
        VStack {
            if let image = image {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
                    .frame(width: 200, height: 200)
            }
            
            Button {
                showScreen.toggle()
            } label: {
                Text("Image Picker")
            }
            .sheet(isPresented: $showScreen) {
                UIImagePickerControllerRepresentable(image: $image, showScreen: $showScreen)
            }

        }
    }
}