Learn Courses My Dashboard

Customizing .contextMenu or Menu

Enclosed code shows

import SwiftUI

struct ErrorSheet: View {
    
    @Environment(\.dismiss) var dismiss
    @State private var showingPopover = false
    
    // properties
    let buttonWidth: Double = 120
    let buttonHeight: Double = 44

        var body: some View {
            
        HStack {
                VStack {
                    Spacer()
                    Image("DefensePositions")
                        .resizable()
                        .scaledToFit()
                    Spacer()
                }
                .edgesIgnoringSafeArea(.bottom)
                .padding(.leading, 5)
                
                Spacer()
                
                VStack {
                    Text("Select Fielder")
                        .font(.title2)
                        .padding()

                    HStack {
                        Button(action: {
                            showingPopover = true
                        },
                           label:{HStack {Image(systemName: "7.circle"); Text("left field")}})
                        .popover(isPresented: $showingPopover) {
                            VStack {
                                
                                    
                            }
                        }
                        .buttonStyle(ButtonWithGrowingEffect(width: buttonWidth, height: buttonHeight, bgColor: Color("IndiansBlue")))
                        /*Button(action: {
                            
                        },
                            label:{HStack {Image(systemName: "8.circle"); Text("center field")}})*/
                        Text("\(Image(systemName: "8.circle")) center field")
                            .contextMenu {
                                    Button {
                                        print("Change country setting")
                                    } label: {
                                        Label("Choose Country", systemImage: "globe")
                                    }

                                    Button {
                                        print("Enable geolocation")
                                    } label: {
                                        Label("Detect Location", systemImage: "location.circle")
                                    }
                            }
                            .buttonStyle(ButtonWithGrowingEffect(width: buttonWidth, height: buttonHeight, bgColor: Color("IndiansBlue")))
                        
                       /* Button(action: {
                            
                            
                        },
                        label:{HStack {Image(systemName: "9.circle"); Text("right field")}})
                            .buttonStyle(ButtonWithGrowingEffect(width: buttonWidth, height: buttonHeight, bgColor: Color("IndiansBlue")))*/
                        Menu("\(Image(systemName: "9.circle")) right field") {
                            Button("Order Now", action: {})
                            Button("Adjust Order", action: {})
                            Button("Cancel", action: {})
                        }
                       .menuStyle(MenuButtonWithGrowingEffect(width: buttonWidth, height: buttonHeight, bgColor: Color("IndiansBlue")))
                       .highPriorityGesture(TapGesture())
                       //.onTapGesture {print("i got tapped")}
                    }
                    
                    
                    HStack {
                        Button(action: {},
                               label:{HStack {Image(systemName: "6.circle"); Text("shortstop")}})
                            .buttonStyle(ButtonWithGrowingEffect(width: buttonWidth, height: buttonHeight, bgColor: Color("IndiansBlue")))
                        Button(action: {},
                               label:{HStack {Image(systemName: "4.circle"); Text("2nd base")}})
                            .buttonStyle(ButtonWithGrowingEffect(width: buttonWidth, height: buttonHeight, bgColor: Color("IndiansBlue")))
                    }
                    
                    HStack {
                        Button(action: {},
                               label:{HStack {Image(systemName: "5.circle"); Text("3rd base")}})
                            .buttonStyle(ButtonWithGrowingEffect(width: buttonWidth, height: buttonHeight, bgColor: Color("IndiansBlue")))
                        Button(action: {},
                               label:{HStack {Image(systemName: "1.circle"); Text("pitcher")}})
                            .buttonStyle(ButtonWithGrowingEffect(width: buttonWidth, height: buttonHeight, bgColor: Color("IndiansBlue")))
                        Button(action: {},
                               label:{HStack {Image(systemName: "3.circle"); Text("1st base")}})
                            .buttonStyle(ButtonWithGrowingEffect(width: buttonWidth, height: buttonHeight, bgColor: Color("IndiansBlue")))
                    }
                    
                    HStack {
                        Button(action: {},
                               label:{HStack {Image(systemName: "2.circle"); Text("catcher")}})
                            .buttonStyle(ButtonWithGrowingEffect(width: buttonWidth, height: buttonHeight, bgColor: Color("IndiansBlue")))
                    }
                    
                    Spacer()
                    
                    Button(action: {
                        dismiss()
                    },
                           label:{Text("Cancel")})
                        .buttonStyle(ButtonWithGrowingEffect(width: buttonWidth, height: buttonHeight, bgColor: Color("IndiansRed")))
                        .padding()
                }
            .edgesIgnoringSafeArea([.bottom])
                
            }
        }
}

struct ErrorSheet_Previews: PreviewProvider {
    static var previews: some View {
        ErrorSheet()
            .previewInterfaceOrientation(.landscapeRight)
    }
}

struct ButtonWithGrowingEffect: ButtonStyle {
    var width: Double
    var height: Double
    var bgColor: Color
    
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .frame(width: width, height: height)
            .foregroundColor(.white)
            .background(bgColor)
            .clipShape(RoundedRectangle(cornerRadius: 10))
            .scaleEffect(configuration.isPressed ? 1.2 : 1)
            .opacity(configuration.isPressed ? 0.6 : 1)
            .animation(.easeOut(duration: 0.2), value: configuration.isPressed)
    }
}

struct MenuButtonStyle: MenuStyle {
    func makeBody(configuration: Configuration) -> some View {
        Menu(configuration)
            .padding()
            .font(.body.bold())
            .foregroundColor(.white)
            .background(
                Capsule()
                    .fill(Color("IndiansBlue"))
            )
            .frame(height: 44)
    }
}

struct MenuButtonWithGrowingEffect: MenuStyle {
    var width: Double
    var height: Double
    var bgColor: Color
    
    func makeBody(configuration: Configuration) -> some View {
        Menu(configuration)
            .frame(width: width, height: height)
            .foregroundColor(.white)
            .background(bgColor)
            .clipShape(RoundedRectangle(cornerRadius: 10))
            .scaleEffect(1) //configuration.isPressed ? 1.2 : 1)
            //.opacity(configuration.isPressed ? 0.6 : 1)
            //.animation(.easeOut(duration: 0.2), value: configuration.isPressed)
    }
}

my Buttons do have a scaleEffect when they get pressed (try pos 6 - the Shortstop)
On the Leftfielder I tried a .popover
on the Centerfielder a .contextMenu
on the Rightfielder a Menu

Now my question: I would love to have the look and feel from the .contextMenu, but the button shall look like the buttons with the scaleEffect when pressed. The longPressAction is inacceptable for me, because the action has to be repeated too often and the user will not automatically understand, that he has to tap long on the button. I’m locking for just a tap. If this is not possible, I would prefer the Menu - version, but the button should scale, when pressed.

Why do I stick on this scale effect thing. I like to show the user that the system has recognized his action. The scaling is a nice way to show him/her.

I don’t find any proper help in the web nor in the documentation. Do you have any ideas or can you show me a way how to achieve this?

Thanks and best regards

Peter

image DefensePosition is enclosed.

Hi Peter,

Just for future reference when you include code in your post…

To format the code nicely, place 3 back-ticks ``` on the line above your code and 3 back-ticks ``` on the line below your code. Like this:

```
Code goes here
```

The 3 back-ticks must be the ONLY characters on the line. The back-tick character is located on the same keyboard key as the tilde character ~ (which is located below the Esc key). You can also highlight an entire code block and click the </> button on the toolbar to wrap the block for you.

This also makes it easier for anyone assisting as they can copy the code and carry out some testing.

hy Chris, thanks for this valuable hint! i have been wondering, how tthe other guys are formatting resp. encapsulating their code …
:grinning: :+1: :pray: Peter

I can’t figure out a solution.

I don’t think the .contextMenu can be customised or changed to respond to a single tap. They are designed to be activated by a long press and they blur the background when the menu options appear. I managed to get the Button to look like the others but that was just by doing this:

ZStack {
    RoundedRectangle(cornerRadius: 10)
        .fill(Color("IndiansBlue"))
        .frame(width: buttonWidth, height: buttonHeight)
    Text("\(Image(systemName: "8.circle")) center field")
        .foregroundColor(.white)
}
.contextMenu {
    Button {
        print("Change country setting")
    } label: {
        Label("Choose Country", systemImage: "globe")
    }

    Button {
        print("Enable geolocation")
    } label: {
        Label("Detect Location", systemImage: "location.circle")
    }
}

I could not get the Menu (right field) button to behave like the others either. If you intercept the tap gesture with an .onTapGesture modifier the menu does not work at all unless you perform a long press.

I don’t know what the answer is.