Malburrito
Malburrito

Reputation: 1198

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

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!

Upvotes: 20

Views: 5345

Answers (2)

MadeByDouglas
MadeByDouglas

Reputation: 2835

Maybe you already figured this out, but the way you're supposed to do it now is to use both .menuStyle(.button) and then .buttonStyle(.bordered) in sequence. They deprecated the borderedButton menuStyle https://developer.apple.com/documentation/swiftui/menustyle/borderedbutton

so once its a button you can style it anyway you like or change its color etc just like a button. I was just thinking of bordered button style as an example.

Upvotes: 2

Andy Jazz
Andy Jazz

Reputation: 58583

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.

Upvotes: 2

Related Questions