Last active
May 31, 2026 15:24
-
-
Save jjrscott/8149cacd2632d0c10dcf23e96d48d51a to your computer and use it in GitHub Desktop.
A SwiftUI view that gives subviews access to a UIKit view controller.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // ViewControllerReader.swift | |
| // | |
| // Created by John Scott on 28/05/2026. | |
| // | |
| import SwiftUI | |
| /// A view that gives subviews access to a UIKit view controller. | |
| /// | |
| /// ⚠️ Do use this unless you absolutely have to; no good can come from treating | |
| /// this a normal thing to do. | |
| /// | |
| /// Note that due to the order of execution the nil UIController used in | |
| /// `makeUIViewController(context:)` is never actually used. Below is the order of | |
| /// execution: | |
| /// | |
| /// `ViewControllerReader.updateUIViewController(_:context:)` > | |
| /// `ViewControllerReader.updateUIViewController(_:context:)` < | |
| /// `ViewControllerReader.sizeThatFits(_:uiViewController:context:)` > | |
| /// `ContentView.body` > | |
| /// `ContentView.body` < | |
| /// `ViewControllerReader.sizeThatFits(_:uiViewController:context:)` < | |
| /// | |
| @available(iOS, introduced: 13.0) | |
| @available(tvOS, introduced: 13.0) | |
| @available(macOS, unavailable) | |
| @available(watchOS, unavailable) | |
| struct ViewControllerReader<Content>: UIViewControllerRepresentable where Content: View { | |
| @ViewBuilder var content: (UIViewController) -> Content | |
| struct ContentView: View { | |
| let uiViewController: UIViewController? | |
| let content: (UIViewController) -> Content | |
| var body: some View { | |
| if let uiViewController { | |
| content(uiViewController) | |
| } | |
| } | |
| } | |
| func makeUIViewController(context: Context) -> UIHostingController<ContentView> { | |
| UIHostingController(rootView: ContentView(uiViewController: nil, content: content)) | |
| } | |
| func updateUIViewController(_ uiViewController: UIHostingController<ContentView>, context: Context) { | |
| uiViewController.rootView = ContentView(uiViewController: uiViewController, content: content) | |
| uiViewController.view.isUserInteractionEnabled = context.environment.isEnabled | |
| } | |
| func sizeThatFits(_ proposal: ProposedViewSize, uiViewController: UIHostingController<ContentView>, context: Context) -> CGSize? { | |
| uiViewController.sizeThatFits(in: proposal.replacingUnspecifiedDimensions()) | |
| } | |
| } | |
| #Preview { | |
| ViewControllerReader { viewController in | |
| Button("Hello, world!") { | |
| let alert = UIAlertController(title: "Hello", message: "World", preferredStyle: .alert) | |
| alert.addAction( | |
| UIAlertAction(title: "OK", style: .default) { _ in } | |
| ) | |
| viewController.present(alert, animated: true) | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment