如果列表顺序更改,SwiftUI NavigationView 中的选择会丢失

las*_*sej 5 swift swiftui swiftui-list swiftui-navigationlink

这是测试数据模型:

class Item: Identifiable {
  let name: String

  init( n: Int) {
    self.name = "\(n)"
  }
}

class Storage: ObservableObject {
  @Published var items = [Item( n: 1), Item( n: 2)]

  func reverse() {
    items = self.items.reversed()
  }
}
Run Code Online (Sandbox Code Playgroud)

这是我的内容视图,其中有一个NavigationLink和一个详细视图,其中有一个反转项目顺序的按钮:

struct ContentView: View {

  @ObservedObject
  var storage = Storage()

  var body: some View {
    NavigationView {
      List {
        ForEach( storage.items) { item in
          NavigationLink( destination: Button( action: {
            self.storage.reverse()
          }) {
            Text("Reverse")
          }) {
            Text( item.name).padding()
          }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我点击或似乎失去了Reverse选择,弹出视图,然后再次推送:NavigationViewList

导航视图失去选择

这是 SwiftUI 中的预期行为还是错误?有解决方法吗?我希望详细视图保持原样,无需重新加载。

paw*_*222 4

您需要id为您的ForEach循环指定一个显式的。

如果您使用静态ForEach(不带id参数),您的视图将被重建,因为数据 ( storage.items) 已更改。

请尝试以下操作:

struct ContentView: View {
    @ObservedObject
    var storage = Storage()

    var body: some View {
        NavigationView {
            List {
                ForEach(storage.items, id:\.name) { item in // <- add `id` parameter
                    NavigationLink(destination: self.destinationView) {
                        Text(item.name).padding()
                    }
                }
            }
        }
    }
    
    var destinationView: some View {
        Button(action: {
            self.storage.reverse()
        }) {
            Text("Reverse")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,此方法仅在保持所选项目的原始位置的情况下才有效。

在此示例中,update()从项目 1 的详细信息屏幕执行不会弹出 NavigationLink。

class Storage: ObservableObject {
    @Published var items = [Item(n: 1), Item(n: 2)]

    func update() {
        items = [Item(n: 1), Item(n: 3)]
    }
}
Run Code Online (Sandbox Code Playgroud)

这是使其工作的解决方法(使用空NavigationLink):

struct ContentView: View {
    @ObservedObject var storage = Storage()
    @State var isLinkActive = false

    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(storage.items, id:\.name) { item in
                        Button(action: {
                            self.isLinkActive = true
                        }) {
                            Text(item.name).padding()
                        }
                    }
                }
                NavigationLink(destination: self.destinationView, isActive: $isLinkActive) {
                    EmptyView()
                }
            }
        }
    }

    var destinationView: some View {
        Button(action: {
            self.storage.reverse()
        }) {
            Text("Reverse")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)