SwiftUI NavigationView 在数据源完成加载时弹出自身

SwiftUI NavigationView pop itself when a datasource is finished loading

假设您在屏幕 A 时正在从网络加载一些数据,这需要一些时间。在等待期间,您可以使用 NavigationLink 导航到其他屏幕。因此,此时您在屏幕 B,然后来自网络的数据已完成加载并将值发送回屏幕 A 中的数据源。NavigationView 会自动弹出,因此您无意中回到了屏幕 A。你有什么主意吗?谢谢

例子

struct ContentView: View {
  @ObservedObject var viewModel = ViewModel()
  
  var body: some View {
    NavigationView {
      List(viewModel.dataSource, id: \.self) { item in
        NavigationLink(destination: Text("\(item)")) {
          Text("\(item)")
            .padding()
        }
      }
    }
  }
}

class ViewModel: ObservableObject {
  @Published private(set) var dataSource = [1, 2, 3, 4, 5]
  
  init() {
    DispatchQueue.main.asyncAfter(deadline: .now() + 3) { // simulate calling webservice
      self.dataSource[0] = 99
    }
  }
}

发生这种情况是因为您将 id 指定为 item 本身,当列表更新时不再有原始项目,因此它关闭

如果您只想修改没有 adding/removing/reordering 的项目,您可以将项目 ID 设为索引:

NavigationView {
    List(viewModel.dataSource.indices, id: \.self) { i in
        let item = viewModel.dataSource[i]
        NavigationLink(destination: Text("\(item)")) {
            Text("\(item)")
                .padding()
        }
    }
}

但是对于更复杂的数据,您需要让您的项目 Identifiable 具有唯一的 ID,这样您就不会遇到这样的问题。看看这个例子:

struct ContentView: View {
    @ObservedObject var viewModel = ViewModel()
    
    var body: some View {
        NavigationView {
            List(viewModel.dataSource) { item in
                NavigationLink(destination: Text("\(item.value)")) {
                    Text("\(item.value)")
                        .padding()
                }
            }
        }
    }
}

class ViewModel: ObservableObject {
    @Published private(set) var dataSource: [Item] = [1, 2, 3, 4, 5]
    
    init() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [self] in // simulate calling webservice
            // you're modifying value but id stays the same
            self.dataSource[0].value = 99
        }
    }
}

struct Item: Identifiable, ExpressibleByIntegerLiteral {
    let id = UUID()
    var value: Int
    
    init(integerLiteral value: IntegerLiteralType) {
        self.value = value
    }
}