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

时间:2020-07-05 09:14:11

标签: 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()
  }
}

这是我的内容视图,其中包含一个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()
          }
        }
      }
    }
  }
}

现在,如果我点击Reverse,则NavigationViewList似乎会丢失选择,弹出视图,然后再次按下:

NavigationView losing selection

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

1 个答案:

答案 0 :(得分:2)

您需要为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")
        }
    }
}

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

在此示例中,从详细信息屏幕为项目1执行update()不会弹出导航链接。

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

    func update() {
        items = [Item(n: 1), Item(n: 3)]
    }
}

这里是一种解决方法,可以使其正常工作(使用空的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")
        }
    }
}