
【Swift】ViewControllerからSwiftUIのViewを表示する
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
SwiftUIからUIViewControllerやUIViewを表示する際は、UIViewControllerRepresentableやUIViewRepresentableを使用することで表示が出来ますが、UIViewControllerからSwiftUIを表示する方法を知りたかったので調べました。
環境
- Xcode 13.3
UIHositngViewControllerを使用する
SwiftUIのViewをUIViewControllerとして使用する為には、UIHostingViewControllerを使用します。
UIHostingViewControllerはSwiftUIのView階層を管理するUIViewControllerです。
SwiftUI ViewをUIKit View階層に統合する場合、UIHostingControllerオブジェクトを作成します。 作成時に、このViewControllerのroot viewとして使用するSwiftUI Viewを指定します。 rootViewのプロパティを使用して、後でそのビューを変更できます。HostingControllerを他のViewControllerと同じように使用するには、インターフェイスにchild ViewControllerとして表示するか、埋め込みます。
使用例
SwiftUIView
例として、下記のSwiftUIのViewを作成しました。このViewをUIViewControllerから表示します。
import SwiftUI
struct SwiftUIView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
ZStack {
Rectangle()
.fill(.yellow)
.ignoresSafeArea()
VStack(spacing: 16) {
Text("SwiftUI View")
Button {
dismiss()
} label: {
Text("画面を閉じる")
}
}
}
}
}
ViewControllerから表示する
UIHostingViewController(rootView:)にSwiftUIViewを指定してインスタンス化したものをpresent(_:, animated:)メソッドを使用するだけのシンプルなものになります。
import UIKit
import SwiftUI
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func presentSwiftUIView() {
let controller = UIHostingController(rootView: SwiftUIView())
controller.modalPresentationStyle = .overFullScreen
present(controller, animated: true)
}
}
デモ
このように表示することが出来ました。

UIKitライフサイクルを使用する
上記までは、表示前にUIHositingController(rootView:)を使用して呼び出すケースでしたが、UIHositingControllerクラスを用意することでライフサイクルを使用することも出来ます。
ViewModelを用意する
今回はviewDidLoadでSwiftUIのViewの背景色の変更を試してみたいと思います。値の変更を反映する為に、@ObsrvableObjectのSwiftUIViewModelを用意しました。
import SwiftUI
import Combine
class SwiftUIViewModel: ObservableObject {
@Published var backgroundColor: Color = .yellow
let subject = PassthroughSubject<Void, Never>()
}
背景色をパブリッシュする為のbackgroundColorと、ボタンの押下イベントを発行する為のsubjectを宣言しています。
SwiftUIView
viewModelの定義をしている為、それに伴い、Rectangle().fill()の箇所と、Buttonの箇所を変更しています。
import SwiftUI
import Combine
struct SwiftUIView: View {
@ObservedObject var viewModel: SwiftUIViewModel
var body: some View {
ZStack {
Rectangle()
.fill(viewModel.backgroundColor)
.ignoresSafeArea()
VStack(spacing: 16) {
Text("SwiftUI View")
Button {
viewModel.subject.send()
} label: {
Text("画面を閉じる")
}
}
}
}
}
SwiftUIViewHostingController
UIHostingControllerをジェネリック型にする際には、ContentとなるSwiftUIのViewの記述が必要です。記載していない場合は、Xcodeが教えてくれます。

import SwiftUI
import Combine
class SwiftUIViewHostingController: UIHostingController<SwiftUIView> {
private let viewModel: SwiftUIViewModel
private var anyCancellable: AnyCancellable?
init(viewModel: SwiftUIViewModel) {
self.viewModel = viewModel
super.init(rootView: SwiftUIView(viewModel: viewModel))
}
required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
viewModel.backgroundColor = .red
anyCancellable = viewModel.subject.sink { [weak self] _ in
self?.dismiss(animated: true)
}
}
}
プロパティ
private let viewModel: SwiftUIViewModel private var anyCancellable: AnyCancellable?
viewModelの値を変更してSwiftUIViewの見た目を変更する為に、SwiftUIViewModelを定義しています。また、ボタンの押下イベントを受け取るので、anyCancellableも用意しています。
init
引数として渡されたviewModelを自身のviewModelとSwiftUIViewのviewModelに渡してUIHostingControllerを初期化しています。
init(viewModel: SwiftUIViewModel) {
self.viewModel = viewModel
super.init(rootView: SwiftUIView(viewModel: viewModel))
}
viewDidLoad
viewDidLoad時に、背景色を.redに変更してみました。そして、ボタンの押下された際には、dismiss(animated:)メソッドを呼び出して画面を破棄するようにしています。
override func viewDidLoad() {
super.viewDidLoad()
viewModel.backgroundColor = .red
anyCancellable = viewModel.subject.sink { [weak self] _ in
self?.dismiss(animated: true)
}
}
ViewControllerから表示する
ViewControllerでpresentSwiftUIView()を実行した際に上記で作成したSwiftUIViewHostingControllerを渡すように変更しました。
import UIKit
import SwiftUI
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func presentSwiftUIView() {
let controller = SwiftUIViewHostingController(viewModel: SwiftUIViewModel())
controller.modalPresentationStyle = .overFullScreen
present(controller, animated: true)
}
}
デモ
無事に背景色が赤色に変更され、ボタン押下で画面を閉じることが出来ました。

おわりに
0からSwiftUIにリプレイスをすることは大変ですが、このように一部をSwiftUIに変更することは簡単に出来ることが判明した為、少しずつSwiftUIに変更していくのも可能だなと感じました。
WWDC22: Use SwiftUI with UIKitのセッションで説明があるのですが、iOS 16からUIHostingConfigurationを使用して、カスタムUICollectionViewやUITableViewセルをシームレスに構築出来るようになるみたいです。楽しみですね!











