Home > Enterprise >  Context Menu not updating in SwiftUI
Context Menu not updating in SwiftUI

Time:11-30

I am trying to setup a SwiftUI .contextMenu with a button that toggle a Bool value. The context menu's button text is supposed to change when the Bool toggles. But the context menu doesn't update. Is there a way to force update the context menu?

Sample code depicting the issue:

import SwiftUI

struct Item: Identifiable {
    let id = UUID()
    let user: String
    var active: Bool
}

struct ContentView: View {
    @State var items: [Item] = [
        Item(user: "Daniel",active: false),
        Item(user: "Jack", active: true),
        Item(user: "John", active: true)
    ]
    
    @State var isOn : Bool = false
    var body: some View {
        List {
            ForEach(items) { item in
                VStack {
                    Text("\(item.user)")
                        
                    HStack {
                        Text("Active ? ")
                        Text(item.active ? "YES": "NO")
                    }
                }.contextMenu{
                    Button(action: {
                        let index = items.firstIndex{$0.user == item.user}
                        
                        items[index!].active.toggle()
                    }) {
                        Text(item.active ? "Set as Active": "Set as Inactive")
                    }
                }
            }
        }
    }
}

CodePudding user response:

It's a bug in SwiftUI, and it is still broken in the simulator of Xcode 13.2 beta 2.

I managed to work around it by duplicating the list item in both branches of an if item.active statement, like this:

struct ContentView: View {
    @State var items: [Item] = [
        Item(user: "Daniel",active: false),
        Item(user: "Jack", active: true),
        Item(user: "John", active: true)
    ]

    var body: some View {
        List {
            ForEach(items) { item in
                // work around SwiftUI bug where
                // context menu doesn't update
                if item.active { listItem(for: item) }
                else { listItem(for: item) }
            }
        }
    }

    private func listItem(for item: Item) -> some View {
        VStack {
            Text("\(item.user)")

            HStack {
                Text("Active ? ")
                Text(item.active ? "YES": "NO")
            }
        }
        .contextMenu {
            Button(item.active ? "Deactivate" : "Activate") {
                if let index = items.firstIndex(where: { $0.id == item.id }) {
                    items[index].active.toggle()
                }
            }
        }
        .id(item.id)
    }
}
  • Related