Skip to content

Instantly share code, notes, and snippets.

@jjrscott
Last active May 31, 2026 15:24
Show Gist options
  • Select an option

  • Save jjrscott/8149cacd2632d0c10dcf23e96d48d51a to your computer and use it in GitHub Desktop.

Select an option

Save jjrscott/8149cacd2632d0c10dcf23e96d48d51a to your computer and use it in GitHub Desktop.
A SwiftUI view that gives subviews access to a UIKit view controller.
//
// 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