I'm building my first SwiftUI app and I've run into a blocker. When a user long-presses one of my cells, I want to show a confirmationdialog with custom buttons.
Here's the code:
.confirmationDialog("", isPresented: $showLongPressMenu) {
Button {
//
} label: {
HStack {
Image(systemName: "checkmark.circle")
Text("Add completion")
}
}
Button {
//
} label: {
HStack {
Image(systemName: "note.text.badge.plus")
Text("Add Note")
}
}
Button("Cancel", role: .cancel) {}
}
This is sort-of working, here's the result:

But what I'm trying to achieve is something like this:

Any pointers would be amazing, thank you.
Here is an approach I'm working on:

struct ContentView: View {
@State private var showConfirmationDialog = false
@State private var showModifierDialog = false
var body: some View {
VStack {
Button("Show Dialog") { showConfirmationDialog = true }
Button("Show ViewMod Dialog") {
withAnimation {
showModifierDialog = true
}
}
.padding()
}
.padding()
// standard confirmationDialog
.confirmationDialog("Test", isPresented: $showConfirmationDialog) {
Button { } label: {
Label("Add completion", systemImage: "checkmark.circle")
}
Button { } label: {
Label("Add Note", systemImage: "note.text.badge.plus")
}
Button("Cancel", role: .cancel) {}
}
// custom confirmationDialog with Icons, Cancel added automatically
.customConfirmDialog(isPresented: $showModifierDialog) {
Button {
// action
showModifierDialog = false
} label: {
Label("Add completion", systemImage: "checkmark.circle")
}
Divider() // unfortunately this is still necessary
Button {
// action
showModifierDialog = false
} label: {
Label("Add Note", systemImage: "note.text.badge.plus")
}
}
}
}
// *** Custom ConfirmDialog Modifier and View extension
extension View {
func customConfirmDialog<A: View>(isPresented: Binding<Bool>, @ViewBuilder actions: @escaping () -> A) -> some View {
return self.modifier(MyCustomModifier(isPresented: isPresented, actions: actions))
}
}
struct MyCustomModifier<A>: ViewModifier where A: View {
@Binding var isPresented: Bool
@ViewBuilder let actions: () -> A
func body(content: Content) -> some View {
ZStack {
content
.frame(maxWidth: .infinity, maxHeight: .infinity)
ZStack(alignment: .bottom) {
if isPresented {
Color.primary.opacity(0.2)
.ignoresSafeArea()
.onTapGesture {
isPresented = false
}
.transition(.opacity)
}
if isPresented {
VStack {
GroupBox {
actions()
.frame(maxWidth: .infinity, alignment: .leading)
}
GroupBox {
Button("Cancel", role: .cancel) {
isPresented = false
}
.bold()
.frame(maxWidth: .infinity, alignment: .center)
}
}
.font(.title3)
.padding(8)
.transition(.move(edge: .bottom))
}
}
}
.animation(.easeInOut, value: isPresented)
}
}
Here's a quick pure SwiftUI custom Dialog I wrote using resultBuilder avoiding the Divider approach:
Package URL: https://github.com/NoeOnJupiter/ComplexDialog/
Usage:
struct ContentView: View {
@State var isPresented = false
@State var color = Color.green
var body: some View {
Button(action: {
isPresented.toggle()
}) {
Text("This is a Test")
.foregroundColor(.white)
}.padding(20)
.background(color)
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
.presentDialog(isPresented: $isPresented, bodyContent: {
HStack {
Image(systemName: "circle.fill")
.foregroundColor(.red)
Text("Red Color")
Spacer()
}.dialogAction {
color = .red
}
HStack {
Image(systemName: "circle.fill")
.foregroundColor(.blue)
Text("Blue Color")
Spacer()
}.dialogAction {
color = .blue
}
HStack {
Image(systemName: "circle.fill")
.foregroundColor(.green)
Text("Green Color")
Spacer()
}.dialogAction {
color = .green
}
}, cancelContent: {
HStack {
Spacer()
Text("Cancel")
.font(.title3)
.fontWeight(.semibold)
Spacer()
}.dialogAction {
}
})
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With