首页 > 解决方案 > 如何从 ObservedObject ViewModel 调用数据加载器方法以显示在 SwiftUI 列表中?

问题描述

给定这个具有 ViewModel 对象的 SwiftUI 类。ViewModel 类有一个由 setDependencies() 方法初始化的依赖项。ViewModel 有一个用空数组初始化的 itemTypes 数组,以及一个 getAllItemType() 方法和一个 getAllItemTypeFromCoreData() 私有方法。getAllItemTypeFromCoreData() 方法从核心数据中填充 itemTypes 数组。

视图模型类:

class ItemTypeViewModel: ObservableObject {
    // MARK: - Binding Properties
    @Published var itemTypes : [ItemType] = []

    // MARK: - Dependency Properties
    var coreDataStack : CoreDataStack!

    func setDependencies(with coreDataStack: CoreDataStack) {
        self.coreDataStack = coreDataStack
    }

    // MARK: - Logic functions
    func getAllItemType() {
        itemTypes = getAllItemTypeFromCoreData()
    }

    // MARK: - Database functions
    private func getAllItemTypeFromCoreData() -> [ItemType] { ...
        do { return try contextToUse.fetch(itemTypesRequest) }
        catch { return [] }
    }
}

SwiftUI 类:

struct ItemTypeView: View {
    @EnvironmentObject var coreDataStack: CoreDataStack

    @ObservedObject var itemTypeViewModel = ItemTypeViewModel()

    var body: some View {
        List {
            Section(header: Text("Items")) {
                ForEach(self.itemTypeViewModel.itemTypes) { itemType in
                    VStack (alignment: .leading) {
                        Text(itemType.name!)
                    }
                }
            .onDelete(perform: delete)
            .onMove(perform: move)
            }
            .onAppear(perform: {
                self.itemTypeViewModel.getAllItemType()    // CASE 1.
            })
        }
        .onAppear(perform: {
            self.itemTypeViewModel.setDependencies(with: self.coreDataStack)
            self.itemTypeViewModel.getAllItemType()    // CASE 2.
        })
    }


    private func move(from source: IndexSet, to destination: Int) {...}

    private func delete(at offsets: IndexSet) {...}
}

struct ItemTypeView_Previews: PreviewProvider {...}

在案例 1 中:每次插入一行时都会调用 itemTypeViewModel.getAllItemTypeFromCoreData()。我的意思是如果表有 X 元素,那么 onAppear 调用 X 次。它调用了每个 onMove 和 onDelete 事件。因此 onMove 和 onDelete 事件不能正常工作。

在案例 2 中: itemTypeViewModel.getAllItemTypeFromCoreData() 在主线程上调用一次,然后以未捕获的异常终止。:

2019-11-15 07:28:05.614884+0100 ItemApp[2123:75091] [TableView] 仅警告一次:UITableView 被告知在视图层次结构中布局其可见单元格和其他内容(表格视图或其superviews 尚未添加到窗口中)。这可能会通过强制表视图中的视图在没有准确信息的情况下加载和执行布局(例如表视图边界、特征集合、布局边距、安全区域插入等)而导致错误,并且还会由于额外的布局传递而导致不必要的性能开销. 在 UITableViewAlertForLayoutOutsideViewHierarchy 处创建一个符号断点,以便在调试器中捕获它并查看导致这种情况发生的原因,因此如果可能的话,您可以完全避免此操作,或者将其推迟到表格视图添加到窗口中。表视图:< _TtC7SwiftUIP33_BFB370BA5F1BADDC9D83021565761A4925UpdateCoalescingTableView: 0x7fb5d81b1c00; 基类 = UITableView; 帧 = (0 0; 0 0); clipsToBounds = YES; 手势识别器 = ; 层 = ; 内容偏移:{0, 0}; 内容大小:{0, 336}; 调整内容插入:{0, 0, 0, 0}; 数据源:<_TtGC7SwiftUIP13$7fff2c6b223419ListCoreCoordinatorGVS_20SystemListDataSourceOs5Never_GOS_19SelectionManagerBoxS2___:0x7fb5d9476950>> 2019-11-15 07:28:05.624903+0100 ItemApp[2123:75091]* Assertion failure in -[_TtC7SwiftUIP33_BFB370BA5F1BADDC9D83021565761A4925UpdateCoalescingTableView _Bug_Detected_In_Client_Of_UITableView_Invalid_Number_Of_Rows_In_Section:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3900.12.15/UITableView.m:2406 2019-11-15 07:28:16.146859+ 0100 ItemApp[2123:75091] *由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:第 0 节中的行数无效。更新后现有节中包含的行数 (7) 必须相等更新前该节中包含的行数 (7),加上或减去从该节插入或删除的行数(7 插入,0 删除),加上或减去移入或移出该节的行数部分(0 移入,0 移出)。

如何通过在 ViewModel 类中保留 getAllItemTypeFromCoreData() 逻辑来解决这个问题?

标签: swiftlistmvvmdependency-injectionswiftui

解决方案


推荐阅读