swift - Swift拆分视图控制器,首次加载时出现“重复”通知
问题描述
完整标题:Swift 4.2 使用拆分视图控制器,首次加载时发出“重复”通知,这会破坏以编程方式在主表视图中选择“新”对象的能力。
背景
我正在使用带有主表和详细表视图的拆分视图控制器 -主表是一个普通的动态表视图,它向用户显示实体列表,详细信息是一个分组的动态表视图,它显示用户的属性和关系值 -主控中的选定实体。
我已经实现了核心数据。
主表视图显示实体列表。主表视图的数据源是一个获取的结果控制器。
明细表视图显示主表视图中当前选定行(实体)的属性和关联关系值。详细表视图的数据源是与主表视图中当前选定行相关的实体,它使用“显示详细信息”segue 传递。
在屏幕较大的设备上splitViewController.isCollapsed == false
,主表视图和详细表视图在屏幕上都处于活动状态,如下图所示。
数据驱动应用程序的相当标准的安排......?
逻辑流程
当用户更新现有实体(在屏幕截图示例中为现有“事件”)时,他们单击Save
详细信息表视图导航栏中的按钮。
因为实体已经存在,所以在 Master Table View Controller 的controller(_:didChange:at:for:newIndexPath)
Fetched Results Controller Delegate 方法中:
在 下
case .update
,实现tableView.reloadRows(at: [indexPath!], with: UITableViewRowAnimation.none)
,这会触发tableView(_willDisplay:forRowAt:)
它依次更新所选表格视图单元格的格式;和在
case .insert
,case .update
和下case .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")
}
}
然后,在 Master Table View Controller 的controllerDidChangeContent(_:)
Fetched Results Controller Delegate 方法中,我执行以下代码...
guard let indexPathOfRowToSelect: IndexPath = indexPathForManagedObjectChanged else {
return
}
indexPathForManagedObjectChanged = nil
tableView.selectRow(at: indexPathOfRowToSelect, animated: false, scrollPosition: UITableViewScrollPosition.none)
这可确保在插入、更新和移动之后,选择与详细表视图中的数据对应的行。
我使用类型的通知UserDefaults.didChangeNotification
。
观察者被添加到主表视图控制器的(EDIT是viewDidLoad
,但现在)viewWillAppear(_:)
方法中。观察者在主表视图控制器的viewWillDisappear(_:)
方法中被移除。
如果用户更改其中一项设置,通知observer
将调用以下函数...
@objc
func userDefaultSettingsDidChange(_ notification: Notification) {
if (notification.object as? UserDefaults) != nil {
NSFetchedResultsController<NSFetchRequestResult>.deleteCache(withName: classCacheName)
fetchedResultsController = nil
tableView.reloadData()
}
}
问题
在主表视图控制器第一次运行时,有一个调用该userDefaultSettingsDidChange
方法的通知,但它在第一次运行获取结果控制器委托方法以响应第一次用户交互后调用此方法。尽管没有更改用户默认设置。
该问题发生在模拟器和设备上。
我添加了一堆print()
s (为了便于阅读代码而删除了)来跟踪以什么顺序发生的事情。
控制台将userDefaultSettingsDidChange
函数记录为在 Fetched Results Controller Delegate 方法完成后执行 - 但仅在第一次运行时执行。
正因为如此,对 的调用reloadData
清除了我之前对 的调用selectRow
。
解决方案
我可以selectRow(at:animated:scrollPosition:)
在 Master Table View Controller 的 Fetched Results Controller Delegate 方法中包装对实例方法的调用,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 中的第一次用户交互造成严重破坏吗?
解决方案
推荐阅读
- c++ - 锁定条件变量后如何运行函数?
- ruby-on-rails - 在 Rails 中使用 .includes() 时,有没有办法在 .select() 列上显式?
- javascript - 如何在嵌入 js 脚本库中测试 DOM 方法?
- asp.net - 在 ASP.Net Core 中对所有 HTTP 请求启用回退的性能损失
- javascript - 期望一个赋值或函数调用,而是看到一个表达式 no-unused-expressions
- java - 如何将 JetBrains Xodus 连接到 S3?
- artificial-intelligence - 泛型算法与传统算法的区别
- excel - 从excel自动过滤器中排除行
- php - 如何有效地将表单之间的价格值从一页转移到另一页
- dart - Flutter 能否免去使用 mac 来创建 IOS 应用程序?