One way to do this might be to declare you own modifier for modal presentation and dismissal.
extension View {
func showModal<T>(_ binding: Binding<Bool>, _ view: @escaping () -> T) -> some View where T: View {
let windowHeightOffset = (UIApplication.shared.windows.first?.frame.height ?? 600) * -1
return ZStack {
self
view().frame(maxWidth: .infinity, maxHeight: .infinity).edgesIgnoringSafeArea(.all).offset(x: 0, y: binding.wrappedValue ? 0 : windowHeightOffset)
}
}
}
Then you can use the modifier on any view that you wish to tell how to display a view and dismiss that view. Just like a popover or sheet modifier.
struct ContentView: View {
@State var showModal = false
var body: some View {
Text("Show").foregroundColor(.blue).onTapGesture {
withAnimation(.easeIn(duration: 0.75)) {
self.showModal = true
}
}.showModal($showModal, {
Text("Dismiss").foregroundColor(.blue).onTapGesture {
withAnimation(.easeIn(duration: 0.75)) {
self.showModal = false
}
}
})
}
}
The presentation is full screen from the top, if you wish it to come from the side, change the transition inside the modifier to leading or trailing. Other transitions would work too, like opacity or scale.
@Environment
or@State
or other "Property Wrappers." This is a shift to the Observer Pattern in a declarative framework, for those who like complicated phrases :-) – Scaliger