Skip to content

Instantly share code, notes, and snippets.

@timothycosta
Last active November 9, 2024 23:09
Show Gist options
  • Save timothycosta/a43dfe25f1d8a37c71341a1ebaf82213 to your computer and use it in GitHub Desktop.
Save timothycosta/a43dfe25f1d8a37c71341a1ebaf82213 to your computer and use it in GitHub Desktop.
Using UIViewController via the SwiftUI Environment
struct ViewControllerHolder {
weak var value: UIViewController?
init(_ value: UIViewController?) {
self.value = value
}
}
struct ViewControllerKey: EnvironmentKey {
static var defaultValue: ViewControllerHolder { return ViewControllerHolder(UIApplication.shared.windows.first?.rootViewController ) }
}
extension EnvironmentValues {
var viewController: ViewControllerHolder {
get { return self[ViewControllerKey.self] }
set { self[ViewControllerKey.self] = newValue }
}
}
extension UIViewController {
func present<Content: View>(presentationStyle: UIModalPresentationStyle = .automatic, transitionStyle: UIModalTransitionStyle = .coverVertical, animated: Bool = true, completion: @escaping () -> Void = {}, @ViewBuilder builder: () -> Content) {
let toPresent = UIHostingController(rootView: AnyView(EmptyView()))
toPresent.modalPresentationStyle = presentationStyle
toPresent.rootView = AnyView(
builder()
.environment(\.viewController, ViewControllerHolder(toPresent))
)
if presentationStyle == .overCurrentContext {
toPresent.view.backgroundColor = .clear
}
self.present(toPresent, animated: animated, completion: completion)
}
}
@tarasis
Copy link

tarasis commented Oct 21, 2020

Thanks for this! I have a couple of questions

Is there a way to make the modal only as big as the content you put in it? I don’t want the white box that appears when using .formSheet/pageSheet. (I know I can just set toPresent.view.backgroundColor = .clear) preferredContentSize seems like an option but haven’t had a chance to test

Also when I tried to add a button to the body of ModalContentView to dismiss (experimenting with full screen), the button appeared behind the content, not above it like I expected given its a VStack. I can’t therefore see how to dismiss full screen

@Sadmansamee
Copy link

@Environment(\.viewController) private var viewControllerHolder: ViewControllerHolder?
private var viewController: UIViewController? {
   self.viewControllerHolder?.value
}

with this code I'm getting error like
Key path value type 'ViewControllerHolder' cannot be converted to contextual type 'ViewControllerHolder?

@haikusw
Copy link

haikusw commented Jan 12, 2021

@Sadmansamee

@Environment(\.viewController) private var viewControllerHolder: ViewControllerHolder?
private var viewController: UIViewController? {
   self.viewControllerHolder?.value
}

with this code I'm getting error like

Key path value type 'ViewControllerHolder' cannot be converted to contextual type 'ViewControllerHolder?

Don't have time to test this but maybe try:

@Environment(\.viewController) private var viewControllerHolder: ViewControllerHolder
private var viewController: UIViewController? {
    self.viewControllerHolder.value
}

Reading the gist, Line 14 defines viewController to return a non-optional ViewControllerHolder so I'm guessing that's the issue you're running into.

@pushpankq
Copy link

and how to dismiss it

@iosdroid
Copy link

iosdroid commented Feb 4, 2022

its support for RTL?

@malhal
Copy link

malhal commented Nov 9, 2024

Using UIViewControllerRepresentable to manage the make/update lifetime will fix the leak.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment