Multiple context menus in a list row

Is it possible to have multiple independent context menus within a list row without these being displayed together as on menu?
I put together a small example to illustrate the question. The desired behavior is that each of the two Text(...) views have their own contextmenu, which is only displayed when pressing that text.
In the example, I can long press anywhere on the list row and will be presented with a context menu with 4 choices, the two menus appended to each other.


struct ContentView: View
{
  let data = [
    "Test 1","Test 2","Test 3","Test 4","Test 5",
    "Test 6","Test 7","Test 8","Test 9","Test 10",
    "Test 11","Test 12","Test 13","Test 14","Test 15",
    "Test 16","Test 17","Test 18","Test 19","Test 20"
  ]
  
  var body: some View
  {
    List
    {
      ForEach(data,id:\.self)
      {
        item in
        HStack
        {
          Text(item+" A")
            .contextMenu
            {
              Button(action: {}) { Text("Item one"); Image(systemName: "globe") }
              Button(action: {}) { Text("Item two"); Image(systemName: "location.circle") }
            }
          Spacer()
          Text(item+" B")
            .contextMenu
            {
              Button(action: {}) { Text("Item one"); Image(systemName: "globe") }
              Button(action: {}) { Text("Item two"); Image(systemName: "location.circle") }
            }
        }
      }
    }
  }
}

Accepted Reply

Yes, one problem is longpress is detected at HStack level, not differently in each Text() view ; even the spacer area reacts to longPress.


But if you replace List by VStack, you get what you want.


struct ContentView: View
{
  let data = [
    "Test  1","Test  2","Test  3","Test  4","Test  5",
    "Test  6","Test  7","Test  8","Test  9","Test 10",
    "Test 11","Test 12","Test 13","Test 14","Test 15",
    "Test 16","Test 17","Test 18","Test 19","Test 20"
  ]
   
  var body: some View
  {
    VStack
    {
      ForEach(data,id:\.self)
      {
        item in
        HStack
        {
            Text(item+" A ")
                .background(Color.blue)
                .contextMenu
                {
                    Button(action: {}) { Text("Item one A \(item)"); Image(systemName: "globe") }
                    Button(action: {}) { Text("Item two A \(item)"); Image(systemName: "location.circle") }
            }
            Spacer()
            Text(item+" B ")
                .background(Color.red)
                .contextMenu
                {
                    Button(action: {}) { Text("Item one B \(item)"); Image(systemName: "globe") }
                    Button(action: {}) { Text("Item two B \(item)"); Image(systemName: "location.circle") }
            }
        }
        .frame(width: 300, height: 40)
      }
    }
  }
}

Replies

If you set the contextMenu as mofifier to HStack, you get menus only once:


struct ContentView: View
{
  let data = [
    "Test 1","Test 2","Test 3","Test 4","Test 5",
    "Test 6","Test 7","Test 8","Test 9","Test 10",
    "Test 11","Test 12","Test 13","Test 14","Test 15",
    "Test 16","Test 17","Test 18","Test 19","Test 20"
  ]
   
  var body: some View
  {
    List
    {
      ForEach(data,id:\.self)
      {
        item in
        HStack
        {
          Text(item+" A")
           
          Spacer()
          Text(item+" B")
           
        }.contextMenu
        {
          Button(action: {}) { Text("Item one"); Image(systemName: "globe") }
          Button(action: {}) { Text("Item two"); Image(systemName: "location.circle") }
        }
      }
    }
  }
}


Is it what you are looking for ?

Or do you want different context menus depending you hit A or B ?

I can see the post wasn't clear enough :-) I want a different menu for each of the Text items. Also, I would like that only the Text item is hightlight when the contextmenu is displayed, not the entire row.
I'm trying to implement a grid combining a List and HStack for each row. Each item in the grid should have it own context menu (or appear like that) as the contents of the menu depends on properties of the item in the grid

Yes, one problem is longpress is detected at HStack level, not differently in each Text() view ; even the spacer area reacts to longPress.


But if you replace List by VStack, you get what you want.


struct ContentView: View
{
  let data = [
    "Test  1","Test  2","Test  3","Test  4","Test  5",
    "Test  6","Test  7","Test  8","Test  9","Test 10",
    "Test 11","Test 12","Test 13","Test 14","Test 15",
    "Test 16","Test 17","Test 18","Test 19","Test 20"
  ]
   
  var body: some View
  {
    VStack
    {
      ForEach(data,id:\.self)
      {
        item in
        HStack
        {
            Text(item+" A ")
                .background(Color.blue)
                .contextMenu
                {
                    Button(action: {}) { Text("Item one A \(item)"); Image(systemName: "globe") }
                    Button(action: {}) { Text("Item two A \(item)"); Image(systemName: "location.circle") }
            }
            Spacer()
            Text(item+" B ")
                .background(Color.red)
                .contextMenu
                {
                    Button(action: {}) { Text("Item one B \(item)"); Image(systemName: "globe") }
                    Button(action: {}) { Text("Item two B \(item)"); Image(systemName: "location.circle") }
            }
        }
        .frame(width: 300, height: 40)
      }
    }
  }
}

Replacing List with VStack looses the scrolling, but wrapping it in a ScrollView takes care of that. Seems to work as I wanted. Strange that List doesn't allow this straight away.
Thanks for the help

Just insert inside a ScrollView:


struct ContentView: View
{
  let data = [
    "Test  1","Test  2","Test  3","Test  4","Test  5",
    "Test  6","Test  7","Test  8","Test  9","Test 10",
    "Test 11","Test 12","Test 13","Test 14","Test 15",
    "Test 16","Test 17","Test 18","Test 19","Test 20"
  ]
   
    var body: some View
    {
        ScrollView {
            VStack  // vs List
                {
                    ForEach(data,id:\.self)
                    {
                        item in
                        HStack
                            {
                                Text(item+" A ")
                                    //                .frame(width: 100.0, height: 20.0)
                                    .background(Color.blue)
                                    .contextMenu
                                    {
                                        Button(action: {}) { Text("Item one A \(item)"); Image(systemName: "globe") }
                                        Button(action: {}) { Text("Item two A \(item)"); Image(systemName: "location.circle") }
                                }
                                Spacer()
                                Text(item+" B ")
                                    //                .frame(width: 100.0, height: 20.0)
                                    .background(Color.red)
                                    .contextMenu
                                    {
                                        Button(action: {}) { Text("Item one B \(item)"); Image(systemName: "globe") }
                                        Button(action: {}) { Text("Item two B \(item)"); Image(systemName: "location.circle") }
                                }
                        }
                        .frame(width: 300, height: 50)
                    }
            }
        }
       
    }
}

I filed a bug report FB7521254