my problem is very simple, i'm trying to implement something similar to the Apple TV app Context menu : appletv+ app example
But in my case, the contextmenu is opening outside of the LazyVGrid and not next to the grid item , and the contextmenu is not getting the context of the selected item, but always the data of the first one : my example
My code :
struct HomeView: View {
var test : [String] = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8", "Item 9", "Item 10", "Item 11", "Item 12", "Item 13", "Item 14"]
var body: some View {
NavigationStack() {
List{
Section(header: Text("Test")
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.primary)) {
ScrollView(.vertical, showsIndicators: false) {
LazyVGrid(columns: [GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())], alignment: .leading, spacing: 80) {
ForEach(test, id: \.self) { item in
Button(item) {
}.buttonStyle(MyButtonStyle()).contextMenu {
Text("Context Menu for : \(item)")
Button("Add Favorite") {}
}
}
}
}
}.contentMargins(30)
}
}
}
}
struct MyButtonStyle: PrimitiveButtonStyle {
@Environment(\.isFocused) var focused: Bool
@State private var isFocused: Bool = false
func makeBody(configuration: Configuration) -> some View {
configuration.label
.compositingGroup()
.frame(minWidth: 250,maxWidth: 250, minHeight: 125, maxHeight: 125)
.padding([.all], 10)
.multilineTextAlignment(.center)
.focusable(true, onFocusChange: { focused in
if focused {
isFocused = true
} else {
isFocused = false
}
})
.background(RoundedRectangle(cornerRadius: 20).fill(isFocused ? .blue : .gray).opacity(0.7))
.foregroundColor(isFocused ? .white : .white)
.onTapGesture(perform: configuration.trigger)
}
}
Rather than using a Button
and trying to add a context menu to it, you could use a Menu
component, and assign a "primary action" which is the behaviour to enact when you press it.
Menu {
Button("Favorite") { ... }
Button("Mark as watched") { ... }
} label: {
// This is where your visual content goes
} primaryAction: {
// this code block is executed on single tap
}
This native component should cut down on the amount of custom code you need to write, and is much more likely to play nicely with LazyVStack
's layout engine.