Home > Net >  How to pass single Buttons into custom ConfirmationDialog
How to pass single Buttons into custom ConfirmationDialog

Time:07-14

I'm working on a custom ConfirmationDialog with icons, which works fine. The downside is that in the view modifier I can only access all buttons as one content container, so I can't put a divider between them, and also can't disable the overlay on button action (I have to do this in the calling closure).

Any ideas how I can pass the single buttons with their actions down into the modifier?

I was playing around with multiple TupleView inits but I'm not sure how to apply them to the modifier and View extension, let alone the button action.

enter image description here

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)
   }
}

CodePudding user response:

Here is first of many tuples (you should support all of them to be compatible with ViewBuilder)

Tested with Xcode 14b3 / iOS 16

extension View {
    func customConfirmDialog<A: View, B: View>(isPresented: Binding<Bool>, @ViewBuilder actions: @escaping () -> TupleView<(A, B)>) -> some View {
        return self.modifier(MyCustomModifier(isPresented: isPresented, actions: {
            let buttons = actions()
            VStack(alignment: .leading) {
                buttons.value.0
                Divider()
                buttons.value.1
            }
        }))
    }
}

demo

  • Related