快速拆分视图控制器,首次加载时出现“重复”通知问题

时间:2018-09-17 09:35:50

标签: swift uitableview core-data notifications nsfetchedresultscontroller

完整标题:Swift 4.2使用拆分视图控制器,在第一次加载时出现“重复”通知,这会破坏在主表视图中以编程方式选择“新”对象的能力。

背景

我正在使用具有主视图和明细表视图的拆分视图控制器-主视图是一种普通的动态表视图,向用户显示实体列表,并提供详细信息是一个分组的动态表视图,显示了主数据库中用户选择的实体的属性和关系值。

我已经实现了核心数据。

主表视图显示实体列表。主表视图的数据源是访存结果控制器。

详细信息表视图显示主表视图中当前所选行(实体)的属性和关联的关系值。明细表视图的数据源是与主表视图中当前所选行相关的实体,使用“显示明细”提示将其传递。

splitViewController.isCollapsed == false屏幕较大的设备上,根据下图,主视图和详细信息表视图均在屏幕上处于活动状态。

Master and Detail table views on screen using iPad Pro 2nd gen simulator

数据驱动应用程序的标准配置...?

逻辑流

当用户更新现有实体(在屏幕快照示例的情况下为现有“事件”)时,他们单击明细表视图的导航栏中的Save按钮。

因为该实体已经存在,所以在主表视图控制器的controller(_:didChange:at:for:newIndexPath)的“提取结果控制器委托”方法中:

    case .update下,
  • 实现tableView.reloadRows(at: [indexPath!], with: UITableViewRowAnimation.none),该事件触发tableView(_willDisplay:forRowAt:),后者又更新所选表视图单元格的格式;和

  • case .insertcase .updatecase .move下,我为当前IndexPath indexPathForManagedObjectChanged

  • 设置了一个临时值

“提取的结果控制器委托”方法:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

    switch type {
        case .insert:
            tableView.insertRows(at: [newIndexPath!], with: .fade)
            indexPathForManagedObjectChanged = newIndexPath
            print("___controllerDidChangeObject: INSERTED OBJECT")
        case .delete:
            tableView.deleteRows(at: [indexPath!], with: .fade)
            print("___controllerDidChangeObject: DELETED OBJECT")
        case .update:
            configureCell(tableView.cellForRow(at: indexPath!)!, withEvent: anObject as! PT_Events)
            indexPathForManagedObjectChanged = indexPath
            tableView.reloadRows(at: [indexPath!], with: UITableView.RowAnimation.none)
            print("___controllerDidChangeObject: UPDATED OBJECT")
        case .move:
            configureCell(tableView.cellForRow(at: indexPath!)!, withEvent: anObject as! PT_Events)
            indexPathForManagedObjectChanged = newIndexPath
            tableView.moveRow(at: indexPath!, to: newIndexPath!)
            print("___controllerDidChangeObject: MOVED OBJECT")
    }
}

然后,在主表视图控制器的controllerDidChangeContent(_:)获取的结果控制器委托方法中,我执行以下代码...

guard let indexPathOfRowToSelect: IndexPath = indexPathForManagedObjectChanged else {
    return
}
indexPathForManagedObjectChanged = nil
tableView.selectRow(at: indexPathOfRowToSelect, animated: false, scrollPosition: UITableViewScrollPosition.none)

这可确保在插入,更新和移动后,选择与“明细表视图”中的数据相对应的行。

我使用的通知类型为UserDefaults.didChangeNotification

将观察者添加到主表视图控制器的viewDidLoad(但现在为viewWillAppear(_:))方法中( EDIT )。在主表视图控制器的viewWillDisappear(_:)方法中删除了观察者。

如果用户更改了其中一项设置,则通知observer将调用以下功能...

@objc
func userDefaultSettingsDidChange(_ notification: Notification) {

    if (notification.object as? UserDefaults) != nil {            
        NSFetchedResultsController<NSFetchRequestResult>.deleteCache(withName: classCacheName)
        fetchedResultsController = nil
        tableView.reloadData() 
    }
}

问题

在主表视图控制器的第一次运行中,有一个调用userDefaultSettingsDidChange方法的通知,但在第一次运行Fetched Results Controller Delegate方法之后,它会响应于第一次用户交互而调用此通知。尽管用户默认设置没有任何变化。

问题发生在模拟器和设备上。

我添加了一堆print()(为了便于阅读代码而删除)以跟踪按什么顺序发生的事情。

在获取结果控制器代表方法完成后,控制台在执行userDefaultSettingsDidChange函数时记录日志-仅在第一次运行时执行。

因此,对reloadData的呼叫会抹去我先前对selectRow的呼叫。

解决方案

我可以将对selectRow(at:animated:scrollPosition:)实例方法的调用包装在主表视图控制器的controllerDidChangeContent(_:)的“提取结果控制器代表”方法中,以包括一些延迟...

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.endUpdates()

    guard let indexPathOfRowToSelect: IndexPath = indexPathForManagedObjectChanged else {
        return
    }
    indexPathForManagedObjectChanged = nil
    let when = DispatchTime.now() + 0.20
    DispatchQueue.main.asyncAfter(deadline: when) {       
        self.tableView.selectRow(at: indexPathOfRowToSelect, animated: false, scrollPosition: UITableView.ScrollPosition.none)
    }
}

这行得通!

但是-我不满意。

我已经读了很多书,但是由于某些原因,似乎无法理解NotificationCenter中正在发生的事情,尽管没有更新,这仍然导致对用户设置的虚幻更新。

有人可以解释一下为什么我看到对用户设置的这种延迟的“更新”仅对UI中的首次用户交互造成严重破坏吗?

0 个答案:

没有答案