更新环境使他的所有国家Swiftui都重新渲染视图

时间:2020-02-04 16:04:51

标签: swiftui combine

我有一个@Environment,它存储所有共享数据供我查看。我的一种观点使用其变量之一来呈现包含可折叠项的列表。为了使列表可折叠,我使用@State是true还是false展开可折叠的内容,在此列表中,我希望用户可以删除其项目,因此我创建了一个函数,用于从@Environment中的变量中删除项目并更新视图。问题在于State更改为其默认值,并且Expanded list变成折叠状态。我想知道如何保留先前的状态,或者告诉我的观点是仅重新渲染实际更改的部分。

这是我的环境对象。我所有的观点都同意这一点。

class GlobalData:ObservableObject {
    @Published var SpoonData: [Item?] = []
    @Published var currentTab: Int = 0

    func deleteSpoonData(at offsets: IndexSet) {
        self.SpoonData.remove(atOffsets: offsets)
    }

    func addSpoonData(item: Item, itemList: Items){
       let exists = SpoonData.firstIndex(where: {$0?.ItemId == item.ItemId})
        var rt = item
        rt.ItemList = itemList

        if exists == nil {
            self.SpoonData.append(rt)
        }
    }
    func deleteProductFromItemList(product: Product, itemId: Int) {
        let fatherIndex = self.SpoonData.firstIndex { item -> Bool in
            item?.ItemId == itemId
        }

        let childIndex = (self.SpoonData[fatherIndex!]?.ItemList?.ItemList?.firstIndex(where: { productfiltered -> Bool in
            productfiltered?.EntryID == product.EntryID
        }))!

        print(self.SpoonData[fatherIndex!]?.ItemList?.ItemList![childIndex])
        self.SpoonData[fatherIndex!]?.ItemList?.ItemList!.remove(at: childIndex)

    }
}

这是显示列表的视图。

    struct SpoonListItemView: View {

    private let viewModel: SpoonListItemViewModel
    @State private var isCollapsed: Bool =  false

    init(viewModel: SpoonListItemViewModel) {
           self.viewModel = viewModel
    }

    var body: some View {
        VStack {
            VStack {
                HStack(alignment: .top) {

                    if self.viewModel.ItemType == 1 {
                        AsyncImage(url: viewModel.ItemImageUrl, size: CGSize(width: 45.0, height: 45.0), contentMode: .fit)
                            .clipShape(Rectangle())
                    } else {
                        AsyncImage(url: viewModel.ItemImageUrl, size: CGSize(width: 45.0, height: 45.0), contentMode: .fit)
                            .clipShape(Circle())
                    }
                    VStack(alignment: .leading) {
                        Text("\(viewModel.ItemName)")
                            .font(.custom("NunitoSans-Bold", size: 14))
                            .lineLimit(1)
                            .multilineTextAlignment(.leading)
                        Text(viewModel.ItemType == 1 ? "Receta" : "Producto")
                            .foregroundColor(Color("textColor"))
                            .font(.custom("NunitoSans-Light", size: 12))
                    }
                    Spacer()
                }.frame(height: 50)
                    .padding(EdgeInsets(top: 0, leading: 24, bottom: 0, trailing: 24)).onTapGesture {
                        self.isCollapsed.toggle()
                }

            }
            if self.viewModel.ItemType == 1 {
                if self.isCollapsed {
                    SpoonListProductView(viewModel: SpoonListProductViewModel(items: self.viewModel.ItemList, father: self.viewModel.ItemId))
                }
            }
        }

        }
}

And this is the view that present the childs when is expanded

    struct SpoonListProductView: View {
    @EnvironmentObject var globalData: GlobalData
    @ObservedObject var viewModel: SpoonListProductViewModel
    @State private var isInEditingMode: Bool = false
    var fatherId: Int?

    init(viewModel: SpoonListProductViewModel) {
            self.viewModel = viewModel
       }

       var body: some View {

        VStack(alignment: .leading) {
            ForEach(viewModel.dataSource) { vm in
                Group{
                    HStack {
                        SpoonProductView.init(viewModel: vm).onLongPressGesture {
                            self.isInEditingMode = true
                        }
                        Spacer()
                        if self.isInEditingMode {
                            Image(systemName: "trash.fill").foregroundColor(Color("textColor")).onTapGesture {
                                self.delete(vm: vm)
                            }
                        }
                    }
                    Divider()
                }
            }

           }.padding(EdgeInsets(top: 8, leading: 8, bottom: 0, trailing: 8))

       }

    func delete(vm: ProductViewModel) {
        //self.viewModel.ItemList?.remove(atOffsets: offsets)
        self.globalData.deleteProductFromItemList(product: vm.product, itemId: self.viewModel.father!)
    }
}

这是列表的代码:

struct SpoonListView: View {
    @EnvironmentObject var globalData: GlobalData
    @ObservedObject var viewModel: SpoonListViewModel
    var items: [Item?]

    init(items: [Item?] ) {
        self.items = items
        self.viewModel = SpoonListViewModel(items: self.items)
    }

    var body: some View {
        SpoonListItemsView(viewModel: viewModel.dataSource!)
    }
}

这是它的viewModel:

class SpoonListViewModel: ObservableObject, Identifiable {
     @Published var dataSource: SpoonListItemsViewModel? = nil
    var items: [Item?]

    init(items: [Item?]) {
        self.items = items
        self.dataSource =    SpoonListItemsViewModel(items: self.items as! [Item])
    }
}

1 个答案:

答案 0 :(得分:0)

这里的问题不是SpoonListItemView,而是女巫认为的。 @State应该在渲染之间保留,但是在这种情况下,List中有ForEachSpoonListView个元素。每次更改后,该列表都会重新呈现。我认为将openIndex状态移到列表中,或者更高级别可以是一种解决方案。

此外,您可以通过用GlobalData覆盖综合来掩盖let objectWillChange = ObservableObjectPublisher()中的事件,但这意味着您必须在其他场合手动调用。所以,我会避免这种情况。

PS:为了更好地理解,您是否还可以添加列表的代码?