본문 바로가기
iOS/SwiftUI

[iOS] SwiftUI와 UIKit 함께 사용하기

by Mildwhale 2019. 12. 10.

SwiftUI가 등장하면서 기존에 사용하던 UIKit은 어떻게 해야 하는지 고민했던 적이 있습니다. UIKit으로 만들어진 앱에 SwiftUI를 사용하거나, SwiftUI로 만들어진 앱에 UIKit 프레임워크를 사용하려면 어떻게 해야 할까요?

다행히도 Apple은 두 프레임워크의 상호 호환을 지원합니다. 이번 글에서는 두 프레임워크를 함께 사용하는 방법에 대해 알아보겠습니다.

SwiftUI to UIkit

iOS 13에서 추가된 UIHostingController를 사용하면, SwiftUI View를 UIKit에서 사용할 수 있습니다.

UIHostingController는 Interfacing with UIKit문서에 다음과 같이 정의되어 있습니다.

UIHostingController is a UIViewController subclass that represents a SwiftUI view within UIKit contexts.

설명 그대로 UIViewController의 서브 클래스이며, init:rootView: 생성자에 SwiftUI의 View를 파라미터로 넘겨주면 UIViewController로 사용할 수 있습니다.

특별히 더 설명할 것이 없기 때문에, 간단한 예제 코드를 소개하면서 다음 주제로 넘어가겠습니다.

// ContentView.swift
struct ContentView: View {
    var body: some View {
        Text("Wayne's World!!!")
    }
}

// AppDelegate.swift
let window = UIWindow(windowScene: windowScene)
let hostingController = UIHostingController(rootView: ContentView())
window.rootViewController = hostingController
window.makeKeyAndVisible()
self.window = window

UIKit to SwiftUI

이번에는 UIKit으로 만들어진 UIView를 SwiftUI에서 사용하는 방법을 소개합니다. 이 방법을 사용하면 기존에 만들어진 멋진 UIKit Framework들을 재사용할 수 있습니다. 😆

UIViewControllerRepresentable

A view that represents a UIKit view controller.

UIViewControllerRepresentable Protocol을 구현하면 UIViewController를 SwiftUI에서 사용할 수 있습니다.

struct ViewControllerRepresentation: UIViewControllerRepresentable {

}

먼저 UIViewControllerRepresentable을 구현하는 struct을 생성합니다. 그럼 Xcode에서 Required 함수를 구현하지 않았다는 에러가 발생합니다.

struct ViewControllerRepresentation: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> ViewController {
        UIStoryboard(name: "Main", bundle: nil)
      .instantiateViewController(withIdentifier: "ViewController")
      as! ViewController
    }

    func updateUIViewController(_ uiViewController: ViewController, context: Context) {

    }
}

위 예시와 같이 makeUIViewController:context: 함수와 updateUIViewController:_:context: 함수를 구현해주면 에러가 사라집니다.

makeUIViewController:context: 함수는 UIViewController를 생성하고 초기화를 수행하는 함수이며, updateUIViewController 함수는 ViewController의 업데이트가 필요할 때 호출됩니다. 이 위치에서 ViewController에 필요한 데이터 또는 정보를 갱신해주어야 합니다.

var body: some View {
    NavigationLink(destination: ViewControllerRepresentation()) {
          Text("Move to UIViewController")
    }
}

ViewControllerRepresentation 구조체를 생성하여 위 예시와 같이 호출해주면, UIViewController를 SwiftUI에서 사용할 수 있습니다!

UIViewRepresentable

UIView도 UIViewRepresentable을 구현하면 SwiftUI에서 손쉽게 사용할 수 있습니다.

UIViewRepresentable Protocol이 제공하는 함수는 이전 항목의 UIViewControllerRepresentable과 동일하기 때문에 자세한 설명은 생략하고, 예제 코드를 소개하고 마무리 하도록 하겠습니다.

struct ColorUISlider: UIViewRepresentable {
    var color: UIColor
    @Binding var value: Double

    func makeUIView(context: Context) -> UISlider {
        let slider = UISlider(frame: .zero)
        slider.thumbTintColor = color
        slider.value = Float(value)
        return slider
    }

    func updateUIView(_ uiView: UISlider, context: Context) {
        uiView.value = Float(self.value)
    }
}

마치며

위에서 설명한 내용을 실습할 수 있는 튜토리얼은 참고자료의 Interfacing with UIKit에 준비되어 있습니다. 이 주제와 이어지는 Data Dependency를 처리할 수 있는 방법과, 다양한 SwiftUI 튜토리얼을 제공하고 있으니 꼭 한번 실습해보시는 것을 추천합니다.

참고자료

Interfacing with UIKit

SwiftUI by Tutorials