Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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
}
like image 850
Mojtaba Hosseini Avatar asked Oct 19 '19 20:10

Mojtaba Hosseini


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!

like image 194
Mojtaba Hosseini Avatar answered Oct 18 '22 19:10

Mojtaba Hosseini


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")
        }
    }
like image 36
pawello2222 Avatar answered Oct 18 '22 20:10

pawello2222


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.

like image 1
RPatel99 Avatar answered Oct 18 '22 21:10

RPatel99


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

like image 1
karmjit singh Avatar answered Oct 18 '22 21:10

karmjit singh