[Swift] UIViewController를 SwiftUI에서 사용하는 법

MoonGoon·2023년 2월 24일
1

Studying Swift

목록 보기
1/2
post-thumbnail

UIViewController를 SwiftUI View에서 사용하는 법

UIViewController를 SwiftUI에서 사용할 수 있는 방법을 찾다가 나중에 까먹었을 때 다시 보려고 포스팅하려고 합니다 :)


UIViewController 는 SwiftUI 가 알아먹지 못하므로 SwiftUI 가 알 수 있게끔 바꿔줘야 합니다.

이를 위해 해주어야 하는 두 가지 것들이 있는데요!

  1. UIViewControllerRepresentable 프로토콜을 만족하는 새로운 SwiftUI view를 만든다.
  2. 이 view에 makeUIViewController()updateUIViewController() 메소드를 구현한다.

우선 SwiftUI View 에 넣기위해 UIViewController 를 만들어줄 필요가 있어요.

class NMapsViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let mapView = NMFMapView(frame: view.frame)
        view.addSubview(mapView)
    }
}

제 경우에는 NaverMap을 그리기 위한 UIViewController를 만들어주었습니다.

그다음 이 UIViewController 를 View 로 바꾸어주기 위한 View를 만들어주어야해요!

UIViewControllerRepresentable

그 전에 UIViewControllerRepresentable 프로토콜을 살펴보면

protocol UIViewControllerRepresentable : View where Self.Body == Never

이렇게 생겼는데 보다시피 View protocol을 채택해고 있고 이 View의 body의 타입은 Never 입니다.

이 Never가 뭔지 구글링을 통해서 찾아봤는데 Swift 3.0부터 들어온 친구라는 설명과 함께 uninhabited type: Never 즉, 상속 받을 수 없는 타입이므로 get을 하거나 instance를 생성할 수 없는 타입이라고 하는데요... 이 부분은 잘 이해가 안가서 다른 예시나 자료를 찾아봐야 할 것 같아요 ;_;

struct MyView: UIViewControllerRepresentable {
    typealias UIViewControllerType = NMapsViewController
    func makeUIViewController(context: Context) -> NMapsViewController {
        let vc = NMapsViewController()
        return vc
    }
    
    func updateUIViewController(_ uiViewController: NMapsViewController, context: Context) {
        
    }
}

이렇게 UIViewControllerRepresentable 프로토콜을 만족하는 MyView를 만들었는데 하나하나 살펴보면

typealias UIViewControllerType = NMapsViewController

이 부분은 이 view가 보여줄 UIViewControllerType을 명시적으로 정의해주는 부분이기 때문에 우리가 보여줄 뷰 컨트롤러를 넣어주면 됩니다.

makeUIViewController

이 부분에서 우리는

  1. view controller 객체를 생성하고
  2. 초기 값을 설정하기

우리의 view 의 경우에는 간단하기 때문에 NMapsViewController 객체를 생성하고 바로 반환해주었습니다.

updateUIViewController

이 메소드는 SwiftUI가 업데이트 되었을때 불리게 되는데요.

우리의 view controller를 업데이트 하기 위한 부분이에요!.

이제 사용하자!

이렇게 메소드 생성까지 끝내면 사용법은 매우 간단한데, 우리가 만들어준 myView를 View 안에 넣어주면 됩니다!

struct ContentView: View {
    @State var recommendedMenu: String = "음식 추천"
    @State var isPresentedMap = false
    private var menuDataManager: MenuDataManager = MenuDataManager()
    
    var body: some View {
        ZStack {
            VStack {
                Spacer()
                Image(recommendedMenu)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 250)
                    .cornerRadius(30)

                Text(recommendedMenu)
                    .font(.system(size: 30))
                    .padding(.bottom, 10)
                Button {
                    isPresentedMap = true
                } label: {
                    Text("GPS")
                }.sheet(isPresented: $isPresentedMap) {
                    MyView()
                }
                
                Spacer()
                Button {
                    recommendedMenu = menuDataManager.recommendMenu()
                } label: {
                    ZStack {
                        RoundedRectangle(cornerRadius: 10)
                            .frame(width: 170, height: 80)
                        Text("음식을 추천해드릴게요!").foregroundColor(.black)
                    }
                    
                }
                Spacer()
                    .frame(height: 100)
            }
            .padding()
        }
    }
}

저는 버튼을 누르면 지도가 있는 sheet가 올라오도록 구현했습니다.

잘 작동되는 모습! 🥳🥳

/// 전체코드
import SwiftUI
import NMapsMap

class NMapsViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let mapView = NMFMapView(frame: view.frame)
        view.addSubview(mapView)
    }
}

struct MyView: UIViewControllerRepresentable {
    typealias UIViewControllerType = NMapsViewController
    func makeUIViewController(context: Context) -> NMapsViewController {
        let vc = NMapsViewController()
        return vc
    }
    
    func updateUIViewController(_ uiViewController: NMapsViewController, context: Context) {
        
    }
}

struct ContentView: View {
    @State var recommendedMenu: String = "음식 추천"
    @State var isPresentedMap = false
    private var menuDataManager: MenuDataManager = MenuDataManager()
    
    var body: some View {
        ZStack {
            VStack {
                Spacer()
                Image(recommendedMenu)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 250)
                    .cornerRadius(30)

                Text(recommendedMenu)
                    .font(.system(size: 30))
                    .padding(.bottom, 10)
                Button {
                    isPresentedMap = true
                } label: {
                    Text("GPS")
                }.sheet(isPresented: $isPresentedMap) {
                    MyView()
                }
                
                Spacer()
                Button {
                    recommendedMenu = menuDataManager.recommendMenu()
                } label: {
                    ZStack {
                        RoundedRectangle(cornerRadius: 10)
                            .frame(width: 170, height: 80)
                        Text("음식을 추천해드릴게요!").foregroundColor(.black)
                    }
                    
                }
                Spacer()
                    .frame(height: 100)
            }
            .padding()
        }
    }
}

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

https://sarunw.com/posts/uiviewcontroller-in-swiftui/
위 링크를 참조했음을 밝힙니당 :)

profile
Swift 개발자를 희망합니다

0개의 댓글