SwiftUI - 嵌套数组通过层次结构绑定值

问题描述 投票:0回答:0

我正在 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)
                }
            }
        }
    }
}
swiftui binding state hierarchical-data
© www.soinside.com 2019 - 2024. All rights reserved.