![](/img/trans.png)
[英]SwiftUI NavigationView jumps back and forth with NavigationLink in ForEach inside List
[英]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>
—— 而不僅僅是一個簡單的布爾條件。 添加$
為您提供了一個到displayedGame
的Binding<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.