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