Swift 用于CollectionView的NSFetchedResultsContollerDelegate
我想在CollectionViewController中使用NSFetchedResultsControllerRelegate。 因此,我刚刚更改了CollectionView的TableViewController的方法Swift 用于CollectionView的NSFetchedResultsContollerDelegate,swift,core-data,delegates,collectionview,Swift,Core Data,Delegates,Collectionview,我想在CollectionViewController中使用NSFetchedResultsControllerRelegate。 因此,我刚刚更改了CollectionView的TableViewController的方法 (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atI
(void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.collectionView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]];
break;
case NSFetchedResultsChangeDelete:
[self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] ];
break;
}
}
(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UICollectionView *collectionView = self.collectionView;
switch(type) {
case NSFetchedResultsChangeInsert:
[collectionView insertItemsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]];
break;
case NSFetchedResultsChangeDelete:
[collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
break;
case NSFetchedResultsChangeUpdate:
[collectionView reloadItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
break;
case NSFetchedResultsChangeMove:
[collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
[collectionView insertItemsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]];
break;
}
}
(void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.collectionView reloadData];
}
但我不知道如何处理CollectionView的TableView的WillChangeContent开始更新和DidChangeContent结束更新
除了我将一个项目从一个部分移动到另一个部分外,其他一切都正常。然后我得到以下错误
这通常是NSManagedObjectContextObjectsIDChangeNotification的观察者中的错误。无效更新:节0中的项目数无效
知道如何解决这个问题吗?将获取的结果控制器与集合视图相结合有点棘手。 这一问题在本文中作了解释 如果你正在寻找如何绕过 NSInternalInconsistenceException运行时异常 UICollectionView,我在GitHub上有一个例子,详细说明了如何排队 来自NSFetchedResultsControllerDelegate的更新 问题在于现有的UITableView类使用BeginUpdate 和endUpdates将批提交到表视图。UICollectionView 有一个新的performbatchudates:method,它接受一个块参数 更新集合视图。这很性感,但效果不好 使用NSFetchedResultsController的现有范例 幸运的是,本文还提供了一个示例实现: 自述文件: 这是一个如何将新UICollectionView与一起使用的示例 NSFetchedResultsController。诀窍是将所做的更新排队 通过NSFetchedResultsControllerDelegate,直到控制器 完成其更新。UICollectionView没有相同的 开始更新和结束更新,UITableView必须让它轻松工作 使用NSFetchedResultsController,您必须对它们进行排队,否则将 内部一致性运行时异常
这是我用Swift实现的。 首先初始化NSBlockOperations数组:
var blockOperations: [NSBlockOperation] = []
在控制器将更改时,重新初始化阵列:
func controllerWillChangeContent(controller: NSFetchedResultsController) {
blockOperations.removeAll(keepCapacity: false)
}
在did change对象方法中:
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
if type == NSFetchedResultsChangeType.Insert {
println("Insert Object: \(newIndexPath)")
blockOperations.append(
NSBlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.insertItemsAtIndexPaths([newIndexPath!])
}
})
)
}
else if type == NSFetchedResultsChangeType.Update {
println("Update Object: \(indexPath)")
blockOperations.append(
NSBlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.reloadItemsAtIndexPaths([indexPath!])
}
})
)
}
else if type == NSFetchedResultsChangeType.Move {
println("Move Object: \(indexPath)")
blockOperations.append(
NSBlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.moveItemAtIndexPath(indexPath!, toIndexPath: newIndexPath!)
}
})
)
}
else if type == NSFetchedResultsChangeType.Delete {
println("Delete Object: \(indexPath)")
blockOperations.append(
NSBlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.deleteItemsAtIndexPaths([indexPath!])
}
})
)
}
}
在did变更部分方法中:
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
if type == NSFetchedResultsChangeType.Insert {
println("Insert Section: \(sectionIndex)")
blockOperations.append(
NSBlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.insertSections(NSIndexSet(index: sectionIndex))
}
})
)
}
else if type == NSFetchedResultsChangeType.Update {
println("Update Section: \(sectionIndex)")
blockOperations.append(
NSBlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.reloadSections(NSIndexSet(index: sectionIndex))
}
})
)
}
else if type == NSFetchedResultsChangeType.Delete {
println("Delete Section: \(sectionIndex)")
blockOperations.append(
NSBlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.deleteSections(NSIndexSet(index: sectionIndex))
}
})
)
}
}
最后,在did控制器中,确实更改了内容方法:
func controllerDidChangeContent(controller: NSFetchedResultsController) {
collectionView!.performBatchUpdates({ () -> Void in
for operation: NSBlockOperation in self.blockOperations {
operation.start()
}
}, completion: { (finished) -> Void in
self.blockOperations.removeAll(keepCapacity: false)
})
}
我个人也在deinit方法中添加了一些代码,以便在ViewController即将解除分配时取消操作:
deinit {
// Cancel all block operations when VC deallocates
for operation: NSBlockOperation in blockOperations {
operation.cancel()
}
blockOperations.removeAll(keepCapacity: false)
}
这里有一点Swift,它与UICollectionViewController的installsStandardGestureForInteractiveMovement配合使用,是一个有点干涸的组件,它会打开installsStandardGestureForInteractiveMovement,以便所有代码路径都很明显。这与绘图代码的总体模式相同
var fetchedResultsProcessingOperations: [NSBlockOperation] = []
private func addFetchedResultsProcessingBlock(processingBlock:(Void)->Void) {
fetchedResultsProcessingOperations.append(NSBlockOperation(block: processingBlock))
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Insert:
addFetchedResultsProcessingBlock {self.collectionView!.insertItemsAtIndexPaths([newIndexPath!])}
case .Update:
addFetchedResultsProcessingBlock {self.collectionView!.reloadItemsAtIndexPaths([indexPath!])}
case .Move:
addFetchedResultsProcessingBlock {
// If installsStandardGestureForInteractiveMovement is on
// the UICollectionViewController will handle this on its own.
guard !self.installsStandardGestureForInteractiveMovement else {
return
}
self.collectionView!.moveItemAtIndexPath(indexPath!, toIndexPath: newIndexPath!)
}
case .Delete:
addFetchedResultsProcessingBlock {self.collectionView!.deleteItemsAtIndexPaths([indexPath!])}
}
}
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
switch type {
case .Insert:
addFetchedResultsProcessingBlock {self.collectionView!.insertSections(NSIndexSet(index: sectionIndex))}
case .Update:
addFetchedResultsProcessingBlock {self.collectionView!.reloadSections(NSIndexSet(index: sectionIndex))}
case .Delete:
addFetchedResultsProcessingBlock {self.collectionView!.deleteSections(NSIndexSet(index: sectionIndex))}
case .Move:
// Not something I'm worrying about right now.
break
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
collectionView!.performBatchUpdates({ () -> Void in
for operation in self.fetchedResultsProcessingOperations {
operation.start()
}
}, completion: { (finished) -> Void in
self.fetchedResultsProcessingOperations.removeAll(keepCapacity: false)
})
}
deinit {
for operation in fetchedResultsProcessingOperations {
operation.cancel()
}
fetchedResultsProcessingOperations.removeAll()
}
我将@Plot的解决方案作为自己的对象,并将其转换为Swift 2
用法:
fetchedResultsController.delegate = CollectionViewFetchedResultsControllerDelegate(collectionView)
Swift 4版本
2019年版Plot的答案:
var blockOperations: [BlockOperation] = []
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
blockOperations.removeAll(keepingCapacity: false)
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
if type == NSFetchedResultsChangeType.insert {
print("Insert Object: \(newIndexPath)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.insertItems(at: [newIndexPath!])
}
})
)
}
else if type == NSFetchedResultsChangeType.update {
print("Update Object: \(indexPath)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.reloadItems(at: [indexPath!])
}
})
)
}
else if type == NSFetchedResultsChangeType.move {
print("Move Object: \(indexPath)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.moveItem(at: indexPath!, to: newIndexPath!)
}
})
)
}
else if type == NSFetchedResultsChangeType.delete {
print("Delete Object: \(indexPath)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.deleteItems(at: [indexPath!])
}
})
)
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
if type == NSFetchedResultsChangeType.insert {
print("Insert Section: \(sectionIndex)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.insertSections(IndexSet(integer: sectionIndex))
}
})
)
}
else if type == NSFetchedResultsChangeType.update {
print("Update Section: \(sectionIndex)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.reloadSections(IndexSet(integer: sectionIndex))
}
})
)
}
else if type == NSFetchedResultsChangeType.delete {
print("Delete Section: \(sectionIndex)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.deleteSections(IndexSet(integer: sectionIndex))
}
})
)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
collectionView!.performBatchUpdates({ () -> Void in
for operation: BlockOperation in self.blockOperations {
operation.start()
}
}, completion: { (finished) -> Void in
self.blockOperations.removeAll(keepingCapacity: false)
})
}
deinit {
// Cancel all block operations when VC deallocates
for operation: BlockOperation in blockOperations {
operation.cancel()
}
blockOperations.removeAll(keepingCapacity: false)
}
2020年版本:
基于上述令人难以置信的答案,并与熟悉的苹果表格示例相匹配:
以熟悉的Apple表视图示例为例:
在标题处
正在将数据更改传递到表视图
所以
下面是类似的模式,用于复制和粘贴集合视图,使用当前语法等
var ops: [BlockOperation] = []
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
ops.append(BlockOperation(block: { [weak self] in
self?.insertItems(at: [newIndexPath!])
}))
case .delete:
ops.append(BlockOperation(block: { [weak self] in
self?.deleteItems(at: [indexPath!])
}))
case .update:
ops.append(BlockOperation(block: { [weak self] in
self?.reloadItems(at: [indexPath!])
}))
case .move:
ops.append(BlockOperation(block: { [weak self] in
self?.moveItem(at: indexPath!, to: newIndexPath!)
}))
@unknown default:
break
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
performBatchUpdates({ () -> Void in
for op: BlockOperation in self.ops { op.start() }
}, completion: { (finished) -> Void in self.ops.removeAll() })
}
deinit {
for o in ops { o.cancel() }
ops.removeAll()
}
我刚刚遗漏了相同的章节材料
在controllerWillChangeContent中不执行任何操作?
在@PhuahYeeKeat的精彩回答中,在controllerWillChangeContent中,ops阵列被清除。我可能错了,但没有理由这么做;它通过批更新周期可靠地清空。只需在controllerWillChangeContent中不执行任何操作
有比赛吗?
我担心在PerformBatchUpdate处理前一批时,如果出现新的didChange,会发生什么情况
我真的不知道performBatchUpdates是否制作了本地副本或什么-在这种情况下,在执行performBatchUpdates之前应该删除全局副本
IDK.谢谢,马丁。我在没有解决方案的情况下尝试了这个方法-没有看到解决方案的更新。现在,通过在集合视图中解决该错误,它终于可以工作了。由于我有页眉和页脚,这是一个非常好的帮助。不过我还是希望这个错误能被解决一次。@aquarius68:它不是一个真正的错误。问题是FRC委托方法和集合视图更新方法实际上并不适合。修复此问题意味着更改或扩展其中一个API。-但我很高兴你让它工作了。我不再收到任何错误消息,但它还没有完全工作;i、 e.如果用户添加了第一个元素,那么它可以工作,但是如果用户添加了第二个元素,那么只有当我返回到包含与集合视图的对象相关的对象的表视图时,它才能工作。@aquarius68:这似乎是一个单独的问题,因此我建议开始一个新问题。然后,更多的人会读到它,或许可以提供帮助。回购协议现在是空的。你可以寄封信吗
ome代码?如果可能,斯威夫特否。节的移动更改类型?@pkamb UICollectionView中没有节的移动。这是“给我”错误扩展不能包含私有变量blockOperations上的存储属性:[BlockOperation]=[]我不知道为什么=\n我没有尝试此操作,但我想知道。这是否有效:fetchedResultsController.delegate=CollectionViewFetchedResultsController DelegateCollectionView?我认为Delegates很弱。这在iOS 13上非常有效,而其他在线解决方案(例如,一个流行的Google结果)在重要操作(如在新设备上进行iCloud同步)期间导致我的UI冻结。我在这里没有看到这些冻结,大概是因为如何处理块操作我不是并发专家,但如果我发现任何问题,我会报告,因为除此之外,FetchedResultsController的CollectionViews出人意料地缺少良好的iOS 13更新资源。谢谢你@Fattie@cdf1982-谢谢,是的,你完全正确。开发这个解决方案花了我们一大笔钱。。。。。。确实是为了准确地解决你概述的问题。是的,这是一个很好的例子,说明你经常在网上看到的示例代码不幸是完全错误的。这是苹果公司一个非常奇怪的角落,他们没有为他们的首要产品CoreData构建一个用于收集视图的解决方案。他们给了蹩脚的旧表视图一个解决方案,但不是集合视图!这只是苹果公司奇怪的事情之一!希望他们能很快做到。我试过这个代码。我遇到异常:尝试将项0插入节0,但在collectionView上更新后,节0中只有0项。didChangeContent函数中的PerformButUpdate只能继续进行标准调试,到处添加打印语句以查找问题编辑器:做得好,我只是简单地添加了缺少的Swift标记,它将修复整个页面
var blockOperations: [BlockOperation] = []
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
blockOperations.removeAll(keepingCapacity: false)
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
if type == NSFetchedResultsChangeType.insert {
print("Insert Object: \(newIndexPath)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.insertItems(at: [newIndexPath!])
}
})
)
}
else if type == NSFetchedResultsChangeType.update {
print("Update Object: \(indexPath)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.reloadItems(at: [indexPath!])
}
})
)
}
else if type == NSFetchedResultsChangeType.move {
print("Move Object: \(indexPath)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.moveItem(at: indexPath!, to: newIndexPath!)
}
})
)
}
else if type == NSFetchedResultsChangeType.delete {
print("Delete Object: \(indexPath)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.deleteItems(at: [indexPath!])
}
})
)
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
if type == NSFetchedResultsChangeType.insert {
print("Insert Section: \(sectionIndex)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.insertSections(IndexSet(integer: sectionIndex))
}
})
)
}
else if type == NSFetchedResultsChangeType.update {
print("Update Section: \(sectionIndex)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.reloadSections(IndexSet(integer: sectionIndex))
}
})
)
}
else if type == NSFetchedResultsChangeType.delete {
print("Delete Section: \(sectionIndex)")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.collectionView!.deleteSections(IndexSet(integer: sectionIndex))
}
})
)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
collectionView!.performBatchUpdates({ () -> Void in
for operation: BlockOperation in self.blockOperations {
operation.start()
}
}, completion: { (finished) -> Void in
self.blockOperations.removeAll(keepingCapacity: false)
})
}
deinit {
// Cancel all block operations when VC deallocates
for operation: BlockOperation in blockOperations {
operation.cancel()
}
blockOperations.removeAll(keepingCapacity: false)
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
insertRows(at: [newIndexPath!], with: .fade)
case .delete:
deleteRows(at: [indexPath!], with: .fade)
case .update:
reloadRows(at: [indexPath!], with: .fade)
case .move:
moveRow(at: indexPath!, to: newIndexPath!)
}
}
var ops: [BlockOperation] = []
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
ops.append(BlockOperation(block: { [weak self] in
self?.insertItems(at: [newIndexPath!])
}))
case .delete:
ops.append(BlockOperation(block: { [weak self] in
self?.deleteItems(at: [indexPath!])
}))
case .update:
ops.append(BlockOperation(block: { [weak self] in
self?.reloadItems(at: [indexPath!])
}))
case .move:
ops.append(BlockOperation(block: { [weak self] in
self?.moveItem(at: indexPath!, to: newIndexPath!)
}))
@unknown default:
break
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
performBatchUpdates({ () -> Void in
for op: BlockOperation in self.ops { op.start() }
}, completion: { (finished) -> Void in self.ops.removeAll() })
}
deinit {
for o in ops { o.cancel() }
ops.removeAll()
}