簡體   English   中英

無法在 List ForEach 中以編程方式激活 NavigationLink

[英]Unable to activate NavigationLink programmatically, in a List ForEach

Github 的一個簡單測試項目中,我顯示了一個 NavigationLink 項目列表:

截屏

文件GamesViewModel.swift模擬了一個數字游戲 ID 列表,在我的真實應用中通過 Websockets 來:

class GamesViewModel: ObservableObject /*, WebSocketDelegate */ {
    @Published var currentGames: [Int] = [2, 3]
    @Published var displayedGame: Int = 0
    
    func updateCurrentGames() {
        currentGames = currentGames.count == 3 ?
            [1, 2, 3, 4] : [2, 5, 7]
    }

    func updateDisplayedGame() {
        displayedGame = currentGames.randomElement() ?? 0
    }
}

我的問題是,當單擊“加入隨機游戲”按鈕時,我試圖以編程方式激活ContentView.swift 中的隨機 NavigationLink:

struct ContentView: View {
    @StateObject private var vm:GamesViewModel = GamesViewModel()

    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(vm.currentGames, id: \.self) { gameNumber in
                        NavigationLink(destination: GameView(gameNumber: gameNumber)
                                       /* , isActive: $vm.displayedGame == gameNumber */ ) {
                            Text("Game #\(gameNumber)")
                        }
                    }
                }
                Button(
                    action: { vm.updateCurrentGames() },
                    label: { Text("Update games") }
                )

                Button(
                    action: { vm.updateDisplayedGame() },
                    label: { Text("Join a random game") }
                )
          }
       }
    }
}

然而,代碼

ForEach(vm.currentGames, id: \.self) { gameNumber in
    NavigationLink(destination: GameView(gameNumber: gameNumber),
                   isActive: $vm.displayedGame == gameNumber ) {
        Text("Game #\(gameNumber)")
    }
}

不編譯:

無法將“Bool”類型的值轉換為預期的參數類型“Binding”

當我嘗試更改為(刪除美元符號)時:

ForEach(vm.currentGames, id: \.self) { gameNumber in
    NavigationLink(destination: GameView(gameNumber: gameNumber),
                   isActive: vm.displayedGame == gameNumber ) {
        Text("Game #\(gameNumber)")
    }
}

它仍然無法編譯!

甚至可以在ForEach使用$嗎?

我的上下文是,在我的真實應用程序中,Jetty 后端在 PostgreSQL 數據庫中創建了一個新游戲,然后將該新游戲的數字 ID 發送到應用程序。 並且應用程序應該顯示該游戲,即以編程方式導航到GameView.swift

問題是您需要一個Binding<Bool> —— 而不僅僅是一個簡單的布爾條件。 添加$為您提供了一個到displayedGameBinding<Int?> (即Binding<Int?> ),但不是Binding<Bool> ,這是NavigationLink期望的。

一種解決方案是為您要查找的條件創建自定義綁定:

class GamesViewModel: ObservableObject /*, WebSocketDelegate */ {
    @Published var currentGames: [Int] = [2, 3]
    @Published var displayedGame: Int?
    
    func navigationBindingForGame(gameNumber: Int) -> Binding<Bool> {
        .init {
            self.displayedGame == gameNumber
        } set: { newValue in
            self.displayedGame = newValue ? gameNumber : nil
        }
    }
    
    func updateCurrentGames() {
        currentGames = currentGames.count == 3 ? [1, 2, 3, 4] : [2, 5, 7]
    }

    func updateDisplayedGame() {
        displayedGame = currentGames.randomElement() ?? 0
    }
}

struct ContentView: View {
    @StateObject private var vm:GamesViewModel = GamesViewModel()

    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(vm.currentGames, id: \.self) { gameNumber in
                        NavigationLink(destination: GameView(gameNumber: gameNumber),
                                       isActive: vm.navigationBindingForGame(gameNumber: gameNumber)
                        ) {
                            Text("Game #\(gameNumber)")
                        }
                    }
                }
                Button(
                    action: { vm.updateCurrentGames() },
                    label: { Text("Update games") }
                ).padding(4)

                Button(
                    action: { vm.updateDisplayedGame() },
                    label: { Text("Join a random game") }
                ).padding(4)
          }.navigationBarTitle("Select a game")
       }
    }
}

我改變displayedGame一個Int? 因為我認為從語義上講,如果沒有顯示的游戲,將其設為 Optional 而不是設置為0更有意義,但是如果需要,可以輕松地將其改回。

你也這樣通過

ForEach(vm.currentGames, id: \.self) { gameNumber in
    NavigationLink(destination: GameView(gameNumber: gameNumber),
                   isActive: .constant(vm.displayedGame == gameNumber)) {
        Text("Game #\(gameNumber)")
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM