43

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
}
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278

4 Answers4

25

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!

Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
11

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")
        }
    }
pawello2222
  • 46,897
  • 22
  • 145
  • 209
1

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.

RPatel99
  • 7,448
  • 2
  • 37
  • 45
  • You answer is mostly fine. Just the Else part is not necessary. – E.Coms Oct 19 '19 at 22:35
  • 5
    You misunderstood my question. I want to configure buttons to be **destructive (red)**, or **disabled (gray and inactive)** – Mojtaba Hosseini Oct 20 '19 at 05:16
  • 1
    @MojtabaHosseini Sorry for misinterpreting! I think SwiftUI has a bug right now where context menu options can't change colors, they can only be black. You might find it beneficial to file a bug report. – RPatel99 Oct 20 '19 at 17:21
  • 2
    To those downvoting: OP changed his question __after__ I posted my answer. The initial wording of the question made it seem like the OP only needed to know how to delete a view with a context menu. I'm not deleting my answer since it is the only answer on this question and I still think it is relevant to the question and could be useful for people. – RPatel99 Nov 14 '19 at 00:13
1

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

pawello2222
  • 46,897
  • 22
  • 145
  • 209
karmjit singh
  • 229
  • 3
  • 14