Контекстное меню не обновляется в SwiftUI

#swift #swiftui #contextmenu #swiftui-contextmenu

Вопрос:

Я пытаюсь настроить SwiftUI .contextMenu с помощью кнопки, которая переключает Bool значение. Текст кнопки контекстного меню должен изменяться при Bool переключении. Но контекстное меню не обновляется. Есть ли способ принудительно обновить контекстное меню?

Пример кода, описывающего проблему:

 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")
                    }
                }
            }
        }
    }
}
 

Ответ №1:

Это ошибка в SwiftUI, и она все еще исправлена в симуляторе бета-версии 2 Xcode 13.2.

Мне удалось обойти это, дублируя элемент списка в обеих ветвях if item.active инструкции, например:

 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)
    }
}