-
-
Save timothycosta/a43dfe25f1d8a37c71341a1ebaf82213 to your computer and use it in GitHub Desktop.
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) | |
} | |
} |
Hi! Thanks so much for the solution, I'm fairly new to swift and I was also running into the memory leak problem but once I had updated the code it seems like there is a problem while building. I ran into a key path value type mismatch error on the @Environment(.viewController) private var viewControllerHolder: UIViewController? line
do you have any idea wha could be the problem? Thanks!
@bobshoemaker wrote:
Hi! Thanks so much for the solution, I'm fairly new to swift and I was also running into the memory leak problem but once I had updated the code it seems like there is a problem while building. I ran into a key path value type mismatch error on the @Environment(.viewController) private var viewControllerHolder: UIViewController? line
do you have any idea wha could be the problem? Thanks!
The .viewController
in @Environment(\.viewController)
refers to the EnvironmentValues
extension's var definition on line 14 of the gist. If you refer to that you will see that it is defined as a ViewControllerHolder
, not a UIViewController
.
(Also you are missing the keypath starting "" in your comment above, but I assume that is just local to this comment and not your code).
So set the type of the var to ViewControllerHolder
and that may make the compiler happy (it's a compiler, so who knows? ;)).
Something like:
@Environment(\.viewController) private var viewControllerHolder: ViewControllerHolder?
Thanks! As you said it's just a compiler issue! I've just changed it to:
@Environment(\.viewController) private var viewControllerHolder: ViewControllerHolder?
private var viewController: UIViewController? {
self.viewControllerHolder?.value
}
The memory leak is gone as well! Thanks again!
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
@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?
@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.
and how to dismiss it
its support for RTL?
Using UIViewControllerRepresentable to manage the make/update lifetime will fix the leak.
I put this code into a simple project, presented a
UIHostingController
fromwindow.rootViewController
and then didself.viewControllerHolder.value?.dismiss(...)
. I don't see any leaks in the memory graph after dismissal.If you didn't directly copy and paste this, ensure that you're declaring
extension EnvironmentValues { var viewController: ViewControllerHolder { ... } }
and notvar viewController: UIViewController? { ... }
as the latter definitely will leak.