How to configure ContextMenu buttons for delete and disabled in SwiftUI?

Viewed 6878

I tried to configure the button in the contextMenu, but it's not working.

Text("A label that have context menu")
    .contextMenu {
        Button(action: {
            // remove it
        }) {
            Text("Remove")
                .foregroundColor(.red) // Not working
            Image(systemName: "trash")
        }.disabled(true) // Not working
    }

what I have:

 Not working appearance

What I'm seeking: (delete and call buttons)

Demo

I would create a UIAction like the following in UIKit but I can't find any modifier or anyway to bring this to the SwiftUI:

let delete = UIAction(title: "Remove", image: UIImage(systemName: "trash"), attributes: .destructive) { action in
    // remove it
}
4 Answers

All of the asked situations are now supported in iOS 15

Destructive: (works from iOS 15)

Set .destructive as the role argument of the button:

Button(role: .destructive) { //  This argument
    // delete something
} label: {
    Label("Delete", systemImage: "trash")
}

Delete Demo


Disabled: (works from iOS 14.2)

Add .disabled modifier to the button.

Button {
    // call someone
} label: {
    Label("Call", systemImage: "phone")
}.disabled(true) //  This modifier

Disabled Demo


Divider: (works from iOS 14)

Use a Divider() view directly.

Divider Demo


Full Demo:

Demo ⚠️ Remember! Do not use image instead of systemImage for showing an SFSymbol on the button!

iOS 15+

Starting with iOS 15 we can add a role to a Button, so it can automatically adapt its appearance:

enter image description here

Here is an example:

Text("A label that have context menu")
    .contextMenu {
        Button(role: .destructive) {
            print("removing...")
        } label: {
            Text("Remove")
            Image(systemName: "trash")
        }
    }

Toggling a boolean that determines if the view is visible works:

struct ContentView: View {
    @State var textVisible = true
    var body: some View {
        Group {
            if textVisible {
                Text("Hello World")
                .contextMenu {
                    Button(action: {
                        self.textVisible = false
                    }) {
                        HStack {
                            Text("Remove")
                            Image(systemName: "trash")
                        }
                    }
                }
            }
        }
    }
}

Of course, since the context menu is attached to the Text that was removed, it will be permanently removed unless you having something else (e.g a Button) that toggles the boolean (textVisible in this case).

Edit: OP wanted to know how to make buttons in the context menu disabled/destructive (grey/red foreground colors), but I believe that as of October 20, 2019, SwiftUI has a bug that doesn't allow any buttons in the context menu to be any color other than red. Otherwise, setting the button as .disabled(true) should give it a grey color and disable it, and setting the button's foreground color to red (foregroundColor(.red)) should make the button destructive.

Apple is promoting use the Menu rather than ContextMenu. If you are looking to disable options in the Menu you can use the following code. You can use expression inside the modifier to make it dynamic

Regarding Styling - Currently it is not possible to style the individual items within the Menu. Even, if you apply the styling it will not work.

struct ContentView: View {
    var body: some View {
        
        VStack {
            Menu("Actions") {
                Button("Duplicate", action: {})
                Button("Rename", action: {})
                Button(action: {}) {
                    
                    Label("Delete", systemImage: "trash")
                    
                }.disabled(true)
                Button(action: {}) {
                   
                    Label("Call", systemImage: "phone")
                    
                }.disabled(true)
            }      
        }
    }
}

Diasbled Menu Options

Related