The ultimate issue here, I believe, is a difference between how the environment is used with regular
View
types vs.
UIViewRepresentable
and friends. In the former you use the
@Environment
property wrapper to do things because the expectation is that everything is being done within the body call, and this seems to be borne out even when looking at, say, the action methods for
Button
s (because you can certainly use the presentation mode from an
@Environment
variable there—likely it's being called from the button's
body
method as a result of the touch handling (maybe on un-highlight, which changes the appearance of the button, at a guess).
Anyway, the important part:
UIViewRepresentable
updates its content in the
updateUIView(_:context:)
method, and one thing that context provides is the current environment. This one can be accessed freely, at least within this method, as any state management has been taken care of by the framework at this point. That isn't necessarily the case for regular
View
types, where I'm guessing the environment isn't expected to be in any particular state outside of the view rendering pipeline.
So, you can do what you need with two things:
- Replace your
@Environment
variable with an @State
variable which is a boolean, set to false
. Call it shouldDismiss
or suchlike. - In your
updateUIView(_:context:)
method, check the value of that state variable. If true
, call context.environment.presentationMode.wrappedValue.dismiss()
, otherwise do the usual thing. Depending on the size of your update method, maybe use a guard
expression to do this, calling dismiss()
and returning immediately.
Here's a rough sketch of what you can use:
struct MyUIView: UIViewRepresentable {
@State fileprivate var shouldDismiss = false
func makeCoordinator() -> Coordinator { Coordinator(self) }
func makeUIView(context: Context) -> UIViewType {
// ...
}
func updateUIView(_ uiView: UIViewType, context: Context) {
guard !shouldDismiss || !context.environment.presentationMode.wrappedValue.isPresented else {
context.environment.presentationMode.wrappedValue.dismiss()
return
}
// ...
}
class Coordinator: NSObject, MyViewDelegate {
var parent: MyUIView
init(parent: MyUIView) {
self.parent = parent
}
func underlyingViewIsDone(_ view: UIView) {
parent.shouldDismiss = true
}
}
}