SwiftUI + UIKit
SwiftUI와 UIKit 같이 사용하기
아직 레거시 코드가 많기 때문에, UIkit 과 SwiftUI 를 같이 사용하는 경우가 있다.
SwiftUI with UIKit
SwiftUI 프로젝트에 UIKit 를 같이 사용하는 방법에 대해서 알아보겠다.
예를들어 Pdf 파일을 볼 수 있는 PDFKit의 PDFView 는 UIKit으로 되어있는데, 이걸 SwiftUI 프로젝트에서 사용하려면, UIViewRepresentable 프로토콜을 사용하면 된다.
struct ViewMe: UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> PDFView {
let pdfView = PDFView()
pdfView.document = PDFDocument(url: url)
return pdfView
}
func updateUIView(_ uiView: PDFView, context: Context) {
}
}
그리고 그 ViewMe 를 SwiftUI 에 추가하면 끝이다.
struct ContentView: View {
let fileURL = Bundle.main.url(forResource: "example", withExtension: "pdf")
var body: some View {
VStack {
ViewMe(url: fileURL!)
}
.padding()
}
}
이번에는 WebKit에 있는, WKWebView() 를 나타내기 위해서 UIViewRepresentable를 사용해보겠다.
struct WebView: UIViewRepresentable {
let url: URL
func makeUIView(context: UIViewRepresentableContext<WebView>) -> WKWebView {
return WKWebView()
}
func updateUIView(_ webView: WKWebView, context: Context) {
let request = URLRequest(url: url)
webView.load(request)
}
}
updateUIView 에서 load(URLRequest) 까지 다 하고나서, SwiftUI 에서는 WebView를 url과 함께 호출하면 된다.
struct ContentView: View {
var body: some View {
WebView(url: URL(string: "https://heroic-horse-c3d1d3.netlify.app")!)
.ignoreSafeArea()
}
}
UIViewRepresentableContext
Contextual information about the state of the system that you use to create and update your UIKit view.
UIViewRepresentableContext는 UIKit 뷰를 만들고 업데이트할 때 사용하는 state 관련한 정보를 담은 MainActor Structure 이다. 우리는 WKWebView 를 반환하고, 자기 자신 WebView 를 넣으면 된다.
@MainActor
struct UIViewRepresentableContext<Representable> where Representable : UIViewRepresentable
UIKit with SwiftUI
이번에는 UIKit 프로젝트에 SwiftUI View 를 호스팅하는 방법을 해보겠다.
Story board
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBSegueAction func openSwiftUIView(_ coder: NSCoder) -> UIViewController? {
return UIHostingController(coder: coder, rootView: SwiftUIView(name: "Nancy"))
}
}
- embeded in nav controller 를 한 스토리보드에서
- UIHostingController 를 만들고,
- Button 으로 show 연결을 하고나서,
- ViewController 에 @IBSegueAction 을 추가했다.
UIHostingController의 rootView 에는 SwiftUI View를 넣으면 된다. (Navigation으로 이동되기 때문에, back button이 보이게 됨.)
참고로, back button을 지우고 싶다면,
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
navigationController?.setNavigationBarHidden(true, animated: false)
}
Code only
스토리보드로 보여주면, 설명이 쉽지 않기에 code only로 다시 한번 더 해보았다.
초기설정
-
Main.storyboard 파일 삭제
-
Info.plist 수정
위 두 key - value 를 삭제한다.
SceneDelegate 수정
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
let viewController = ViewController()
let navController = UINavigationController(rootViewController: viewController)
window?.rootViewController = navController
window?.makeKeyAndVisible()
}
import UIKit
import SwiftUI
class ViewController: UIViewController {
let button: UIButton = {
let button = UIButton(type: .system)
var config = UIButton.Configuration.plain()
config.title = "Button"
button.configuration = config
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
// navigationController?.setNavigationBarHidden(true, animated: false)
title = "ViewController"
view.backgroundColor = .white
view.addSubview(button)
button.addAction(UIAction { [weak self] action in
let hostingController = UIHostingController(rootView: SwiftUIView(name: "Nancy"))
self?.navigationController?.pushViewController(hostingController, animated: true)
}, for: .touchUpInside)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
}
이렇게 SwiftUI와 UIKit 을 통합하는 방법에 대해서 알아보았다.