SwiftUI: Is there any way to add a custom button style to a Menu()?

Viewed 1077

I have been trying for quite some time to add a custom button style/behavior to the label of a SwiftUI Menu() but haven't had any success so far. Here is an example of what I want to achieve:

Custom Button Style

Let's assume I have a custom Button Style that lets a button's text become red when pressed.

struct RedButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .foregroundColor(configuration.isPressed ? .red : .primary)
}

Menu

Also, I have a standard SwiftUI menu.

Menu("Menu button", content: {
    // Some menu content goes here
})

In this configuration the SwiftUI menu displays a standard button in the accent color (system blue) that behaves like a standard button (getting greyed out when pressed). When pressed, the menu is shown as expected.

Custom menu button

Now I want to somehow apply the custom button style to the button the menu is using. What I tried was the following:

Menu(content: {
    // Some menu content goes here
}, label: {
    Button("Menu button", action: { })
        .buttonStyle(RedButtonStyle())
})

However, the behavior of the menu's button is not changing at all - it's still just getting greyed out when being pressed, not getting colored red.

I also experimented a bit with creating a custom MenuStyle but haven't had any success so far.

Any suggestions on this? Thanks!

1 Answers

Menu's label color

In iOS 15.0+, there's the init(_:role:action:) initializer that allows you create a red label color using .destructive role. If you're not intending to delete user data, use this role for your button. Here's how the code may look like:

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        ZStack {
            Color.black.ignoresSafeArea()
            
            Menu("Actions") {
                Button(role: .destructive, action: { }) {
                    Label("Apple Swift 6.0", systemImage: "swift")
                        .font(.largeTitle)
                }
            }
            .font(.largeTitle)
            .foregroundColor(.yellow)              
        }
    }
}

enter image description here

Menu's button colors

Here's my workaround for changing Menu's button text color.

import SwiftUI

struct ContentView: View {
    
    @State private var color: Color = .red
    
    var body: some View {
        
        let press = LongPressGesture(minimumDuration: 0.0001)
                                         .onChanged { _ in color = .yellow }
                                         .onEnded { _ in color = .red }

        ZStack {
            Color.black.ignoresSafeArea()
            
            Menu("Choose") {
                Button(role: .none, action: { }) {
                    Label("Apple Swift 6.0", systemImage: "swift")
                }
            }
            .font(.largeTitle)
            .foregroundColor(color)
            .gesture(press)
        }
    }
}

enter image description here

P.S.

Tested on iOS 15.5 and macOS 12.4.

Related