I'm trying to create a modal containing a Form in SwiftUI. I'd prefer not to use action sheets in favor of a modal similar to the one pictured below. It seems as though SwiftUI Modals and Alerts aren't capable of such modals yet, is there a possible solution?
Here are two examples of ways to create a custom modal.
.overlay()
import SwiftUI
struct ContentView: View { // your main view
@State var showModal: Bool = false
var body: some View {
NavigationView {
Button(action: {
self.showModal.toggle()
}) {
HStack {
Image(systemName: "plus.circle.fill")
.imageScale(.large)
Text("Show Modal")
}
}
.navigationBarTitle("Welcome")
}
.navigationViewStyle(StackNavigationViewStyle())
.overlay(ModalView(showModal: $showModal))
}
}
struct ModalView: View { // draws a semi-transparent rectangle that contains the modal
@Binding var showModal: Bool
var body: some View {
Group {
if showModal {
Rectangle()
.foregroundColor(Color.black.opacity(0.5))
.edgesIgnoringSafeArea(.all)
.overlay(
GeometryReader { geometry in
RoundedRectangle(cornerRadius: 16)
.foregroundColor(.white)
.frame(width: min(geometry.size.width - 100, 300), height: min(geometry.size.height - 100, 200))
.overlay(ModalContentView(showModal: self.$showModal))
}
)
}
}
}
}
struct ModalContentView: View { // the real modal content
@Binding var showModal: Bool
var body: some View {
VStack {
Text("Modal Content")
Button(action: {
self.showModal.toggle()
}) {
HStack {
Image(systemName: "xmark.circle.fill")
.imageScale(.large)
Text("Close Modal")
}
}
}
}
}
struct ContentView: View {
@State var showModal: Bool = false
var body: some View {
NavigationView {
ZStack {
Button(action: {
withAnimation {
self.showModal.toggle()
}
}) {
HStack {
Image(systemName: "plus.circle.fill")
.imageScale(.large)
Text("Show Modal")
}
}
if showModal {
Rectangle() // the semi-transparent overlay
.foregroundColor(Color.black.opacity(0.5))
.edgesIgnoringSafeArea(.all)
GeometryReader { geometry in // the modal container
RoundedRectangle(cornerRadius: 16)
.foregroundColor(.white)
.frame(width: min(geometry.size.width - 100, 300), height: min(geometry.size.height - 100, 200))
.overlay(ModalContentView(showModal: self.$showModal))
}
.transition(.move(edge: .bottom))
}
}
.navigationBarTitle("Welcome")
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct ModalContentView: View {
@Binding var showModal: Bool
var body: some View {
VStack {
Text("Modal Content")
Button(action: {
withAnimation {
self.showModal.toggle()
}
}) {
HStack {
Image(systemName: "xmark.circle.fill")
.imageScale(.large)
Text("Close Modal")
}
}
}
}
}
Maybe this second approach is even better. With this one, I also got the animation a kind of working.