【Xcode/Swift】SwiftUIで作ったViewをUIKitから表示させる

色々なやり方があるっぽく、何が正解かわかりませんが自分なりに出来た実装を共有します。

Contents 非表示

実装

View側

ImageやColorはSwiftGenを使用しているので、真似して作る場合は画像と色はテキトーにオリジナルのものを入れればOKです。

import SwiftUI

struct SampleSwiftUIView: View {
    @State var dismissAction: (() -> Void)

    var body: some View {
        VStack {
            closeButton
            sampleView
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
        .padding(12)
        .background(
            Color.black
                .opacity(0.5)
                .ignoresSafeArea()
        )
        .onTapGesture {
            dismissAction()
        }
    }

    var closeButton: some View {
        HStack {
            Spacer()
            Button(action: dismissAction) {
                Asset.Assets.imgHeadBack.swiftUIImage
                    .resizable()
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
            }
            .frame(width: 40, height: 40)
            .padding(2)
        }
    }

    var sampleView: some View {
        VStack(spacing: 16) {
            Text("This is SwiftUI View!!!")
                .font(.system(size: 24, weight: .bold))
                .foregroundColor(.black)
            Asset.Assets.imgApple.swiftUIImage
                .resizable()
                .scaledToFit()
                .padding(.horizontal, 80)
            mainCloseButton
        }
        .padding(20)
        .background(.white)
        .frame(maxWidth: .infinity)
        .cornerRadius(12)
    }

    var mainCloseButton: some View {
        Button(action: {
            dismissAction()
        }, label: {
            Text("Close")
                .font(.system(size: 24, weight: .bold))
                .foregroundColor(.white)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
        })
        .background(Asset.Colors.mainColor.swiftUIColor)
        .cornerRadius(80)
        .frame(height: 56)
    }
}

struct SampleSwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        SampleSwiftUIView(dismissAction: {})
    }
}

ロジック側

import UIKit

class HomeViewController: UIViewController {

    // MARK: - Properties
    @IBOutlet private weak var showSwiftUIViewButton: UIButton!

    // MARK: - View Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func showSwiftUIView(_ sender: Any) {
        let view = SampleSwiftUIView(
            dismissAction: { [weak self] in
                self?.dismiss(animated: true)
            }
        )
        let hostingController = HostingController(rootView: view)
        hostingController.modalPresentationStyle = .overFullScreen
        hostingController.modalTransitionStyle = .crossDissolve
        self.present(hostingController, animated: true)
    }
}
import UIKit
import SwiftUI

final class HostingController<T: View>: UIHostingController<T> {

    // MARK: - Initialize
    override init(rootView: T) {
        super.init(rootView: rootView)
        view.backgroundColor = .clear
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}