我正在 SwiftUI 中实现一个盘点应用程序,目前我有一个三层深度模型。级别 1/view1 是 [StockTake] 的列表。级别 2/view2 是 [StockCount] 的列表,最后级别 3/view3 显示 [StockProduct] 数组中的所有产品。简化结构:
class StockTakeViewModel: ObservableObject {
@Published var stockTakes = [StockTake]()
}
struct StockTake {
var stockCounts = [StockCount]()
}
struct StockCount {
var stockProducts = [StockProduct]()
}
struct StockProduct {}
我需要将项目添加到 View3 中的 stockProducts 数组。这应该反映在所有三个视图中。但是,由于我在 View2 和 View3 中使用 Binding,因此我在屏幕上看不到 stockProducts 数组的新添加(View3 在添加到 stockProducts 数组时不会更新)。直到我导航回 View1,然后向下导航到 View2,最后导航到 View3。
所以问题是,我怎样才能将单一的真实来源保持在第一层,同时能够更新数据和三层深度的视图?是否可以强制 View2 和 View3 重新渲染。不喜欢这个主意,我错过了什么?
完整的源代码可以在这里下载:https://github.com/igunther/Levels
型号:
class StockTakeViewModel: ObservableObject {
@Published var stockTakes = [StockTake]()
init() {
let stockTake1 = StockTake(name: "Stocktake1",
stockCounts: [.init(employee: "John Doe",
stockProducts: [.init(productName: "Initial Product1", counted: 1),
.init(productName: "Initial Product2", counted: 1),
.init(productName: "Initial Product3", counted: 1),
.init(productName: "Initial Product4", counted: 1)])])
self.stockTakes.append(stockTake1)
}
}
struct StockTake: Identifiable, Hashable {
var id = UUID()
var name: String
var counted: Int32 {
get {
return stockCounts.reduce(0) { $0 + $1.counted }
}
}
var stockCounts = [StockCount]()
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
struct StockCount: Identifiable, Hashable {
var id = UUID()
var employee: String
var counted: Int32 {
get {
return stockProducts.reduce(0) { $0 + $1.counted }
}
}
var stockProducts = [StockProduct]()
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
struct StockProduct: Identifiable, Hashable {
var id = UUID()
var productName: String
var counted: Int32 = 0
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
意见
struct ContentView: View {
@ObservedObject var stockTakeViewModel = StockTakeViewModel()
var body: some View {
NavigationStack {
StockTakesView(stockTakes: $stockTakeViewModel.stockTakes)
}
}
}
/// View 1
struct StockTakesView: View {
@Binding var stockTakes: [StockTake]
var body: some View {
VStack {
Text("StockTakesView: View 1")
.font(.title2)
LazyVStack {
ForEach($stockTakes, id: \.self) { $stockTake in
NavigationLink {
StockTakeView(stockTake: $stockTake)
} label: {
Text("\(stockTake.name) - Counted: \(stockTake.counted) ")
}
}
}
}
}
}
/// View 2
struct StockTakeView: View {
@Binding var stockTake: StockTake
var body: some View {
VStack {
Text("StockTakeView: View 2")
.font(.title)
Text("\(stockTake.name) - Counted: \(stockTake.counted) ")
LazyVStack {
ForEach($stockTake.stockCounts, id: \.self) { $stockCount in
NavigationLink {
StockCountsView(stockCount: $stockCount)
} label: {
Text(stockCount.employee)
}
}
}
}
}
}
/// View 3
struct StockCountsView: View {
@Binding var stockCount: StockCount
@State private var productNumber: Int = 0
var body: some View {
VStack {
Text("StockCountsView: View 3")
.font(.title)
Text("Counted: \(stockCount.counted)")
Button {
productNumber += 1
let stockProduct = StockProduct(productName: "New Product \(productNumber)", counted: 1)
stockCount.stockProducts.append(stockProduct)
} label: {
Text("Add product")
}
LazyVStack {
ForEach(stockCount.stockProducts) { stockProduct in
Text(stockProduct.productName)
}
}
}
}
}