SwiftUI:如何更新NavigationView详细信息屏幕?

时间:2020-04-28 16:17:34

标签: rest swiftui

我有一个应用程序,可从REST后端服务器获取车辆列表。然后,它使用该列表构建可以点击以显示其中一辆车的详细信息的车辆列表:

@State private var selectedVehicle: Vehicle?
@Binding var vehicles: [Vehicle]

List {
  NavigationView {

    ForEach( vehicles ) { vehicle in
            NavigationLink( destination: VehicleDetailScreen( vehicle: vehicle ),
                            tag: vehicle,
                            selection: self.$selectedVehicle ) {
                                Text( vehicle.name )
            }
    }
  }
}

struct VehicleDetailScreen: View {    
    var vehicle: Vehicle

    var body: some View {
       // Lots of rendering code omitted
    }

}

到目前为止,太好了。这很好。当我们从服务器获取更新的信息时,就会出现问题。更新绑定的vehicles属性非常适合更新列表。但是详细信息屏幕仍然显示不再相关的数据。

我的第一个想法只是将详细视图从NavigationView中弹出。不幸的是,SwiftUI并没有提供我可以在iPad上的两栏视图中找到的可靠方法。

我的下一个想法是,我们也需要将车辆作为@Binding传递到VehicleDetailScreen,以便我们可以对其进行更新。但这也很难做到,因为我们需要引用该绑定,以便将更新的值塞入其中。我能想到的唯一方法是完全重做我们的网络并为对象代码建模,以使其像CoreData一样工作,将对象保留在内存中,并使用服务器上的新值更新它们,而不是生成新对象。这将是很大的努力,而且如果有其他选择,显然我也不希望这样做。

所以我对此有些困惑。任何想法/想法/建议都非常欢迎!

2 个答案:

答案 0 :(得分:2)

也许@Binding的概念有些令人困惑。从@State var(父视图)到@Binding var(子视图)。 一个struct的哈希值,用于简化数组[Vehicle]的元素并对其重新排序。

类似这样的东西:

struct Vehicle: Hashable {
    var name:String
    //var otherItem: Any
}

struct ContentView: View {

    @State var vehicle: Vehicle //the struct of your REST
    @State var vehicles: [Vehicle] // the array of your REST

    var body: some View {
        List {
          NavigationView {
            ForEach(vehicles, id:\.self) { item in // loop the array to get every single item conform to the struct
                NavigationLink( destination: VehicleDetailScreen(vehicle: self.$vehicle)) { // here to pass the binding
                    Text("\(self.vehicle.name)")
                }
            }
          }
        }
    }
}

//detail view
struct VehicleDetailScreen: View {
    @Binding var vehicle: Vehicle // here the binding
    var body: some View {
        Text("\(vehicle.name)")
    }
}

答案 1 :(得分:1)

如果要在数据更改时更新详细视图,则必须使用绑定。

就架构而言,我建议创建所谓的 Stores ,其中包含可在多个视图中使用的数据。结合使用一些静态的Stores提供程序,可以轻松地在任何地方访问和修改数据,并让视图自动更新。

使用UIKit时,您可以通过调用reloadTable来手动刷新数据。在SwiftUI中,这没有完成。假设您可以手动触发视图更新,但是我建议不要这样做,因为这不是SwiftUI的意图。

我已修改您的代码以显示此示例:

class StoreProvider {
    static let carStore = CarStore()
}

class CarStore: ObservableObject {
    @Published var vehicles: [Vehicle] = [Vehicle(id: "car01", name: "Porsche", year: 2016), Vehicle(id: "car02", name: "Lamborghini", year: 2002)]
}

struct Vehicle: Identifiable, Hashable {
    let id: String
    var name: String
    var year: Int
}

struct CarOverview: View {
    @ObservedObject var store = StoreProvider.carStore
    @State var selectedVehicle: Vehicle?

    var body: some View {
        NavigationView {
            List {
                ForEach(store.vehicles.indices) { vehicleIndex in
                    NavigationLink(destination: VehicleDetailScreen(vehicle: self.$store.vehicles[vehicleIndex])) {
                        Text(self.store.vehicles[vehicleIndex].name)
                    }.onTapGesture {
                        self.selectedVehicle = self.store.vehicles[vehicleIndex]
                    }
                }
            }
        }
    }
}

struct VehicleDetailScreen: View {
    @Binding var vehicle: Vehicle

    func updateValues() {
        vehicle.year = Int.random(in: 1990..<2020)
    }

    var body: some View {
        VStack {
            Text(vehicle.name)
            Text("Year: ") + Text(vehicle.year.description)
        }.onTapGesture(perform: updateValues)
    }
}