CollectionViewController.m line 439
__50-[CollectionViewController photoLibraryDidChange:]_block_invoke
致命异常:NSInternalInconsistencyException
尝试删除并重新加载相同的索引路径({length = 2,path = 0 – 26007})
- (void)photoLibraryDidChange:(PHChange *)changeInstance
{
// Call might come on any background queue. Re-dispatch to the main queue to handle it.
dispatch_async(dispatch_get_main_queue(),^{
// check if there are changes to the assets (insertions,deletions,updates)
PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.assetsFetchResults];
if (collectionChanges) {
// get the new fetch result
self.assetsFetchResults = [collectionChanges fetchResultAfterChanges];
UICollectionView *collectionView = self.collectionView;
if (![collectionChanges hasIncrementalChanges] || [collectionChanges hasMoves]) {
// we need to reload all if the incremental diffs are not available
[collectionView reloadData];
} else {
// if we have incremental diffs,tell the collection view to animate insertions and deletions
[collectionView performBatchUpdates:^{
NSIndexSet *removedindexes = [collectionChanges removedindexes];
if ([removedindexes count]) {
[collectionView deleteItemsAtIndexPaths:[removedindexes aapl_indexPathsFromIndexesWithSection:0]];
}
NSIndexSet *insertedindexes = [collectionChanges insertedindexes];
if ([insertedindexes count]) {
[collectionView insertItemsAtIndexPaths:[insertedindexes aapl_indexPathsFromIndexesWithSection:0]];
}
NSIndexSet *changedindexes = [collectionChanges changedindexes];
if ([changedindexes count]) {
[collectionView reloadItemsAtIndexPaths:[changedindexes aapl_indexPathsFromIndexesWithSection:0]];
}
} completion:NULL];
}
[self resetCachedAssets];
}
});
}
来源:https://developer.apple.com/devcenter/download.action?path=/wwdc_2014/wwdc_2014_sample_code/exampleappusingphotosframework.zip
我无法复制这个问题.可能是什么问题呢?非常感谢!
解决方法
>打开正在监听更改的应用程序
>打开照片应用程序,将一组照片从iCloud共享相册保存到您的照片库
>转到照片应用程序,删除其中的一些照片
>再次转到iCloud共享相册,并再次保存您删除的一些照片.你会看到这种情况发生.
我发现一个更新的代码似乎在这里更好地处理更新行为:
https://developer.apple.com/library/ios/documentation/Photos/Reference/PHPhotoLibraryChangeObserver_Protocol/
但是它仍然不会处理这种情况,也不会在要删除的索引更大时(即由于未捕获的异常’NSInternalInconsistencyException’终止应用程序),原因是:尝试从第0部分删除第9项,其中只包含9个更新前的项目).我创建了这个代码更新版本,处理这个更好,迄今为止还没有为我崩溃.
func photoLibraryDidChange(changeInfo: PHChange!) {
// Photos may call this method on a background queue;
// switch to the main queue to update the UI.
dispatch_async(dispatch_get_main_queue()) {
// Check for changes to the list of assets (insertions,moves,or updates).
if let collectionChanges = changeInfo.changeDetailsForFetchResult(self.assetsFetchResult) {
// Get the new fetch result for future change tracking.
self.assetsFetchResult = collectionChanges.fetchResultAfterChanges
if collectionChanges.hasIncrementalChanges {
// Get the changes as lists of index paths for updating the UI.
var removedpaths: [NSIndexPath]?
var insertedpaths: [NSIndexPath]?
var changedpaths: [NSIndexPath]?
if let removed = collectionChanges.removedindexes {
removedpaths = self.indexPathsFromIndexSetWithSection(removed,section: 0)
}
if let inserted = collectionChanges.insertedindexes {
insertedpaths = self.indexPathsFromIndexSetWithSection(inserted,section: 0)
}
if let changed = collectionChanges.changedindexes {
changedpaths = self.indexPathsFromIndexSetWithSection(changed,section: 0)
}
var shouldReload = false
if changedpaths != nil && removedpaths != nil{
for changedpath in changedpaths!{
if contains(removedpaths!,changedpath){
shouldReload = true
break
}
}
}
if removedpaths?.last?.item >= self.assetsFetchResult.count{
shouldReload = true
}
if shouldReload{
self.collectionView.reloadData()
}else{
// Tell the collection view to animate insertions/deletions/moves
// and to refresh any cells that have changed content.
self.collectionView.performBatchUpdates(
{
if let theRemovedpaths = removedpaths {
self.collectionView.deleteItemsAtIndexPaths(theRemovedpaths)
}
if let theInsertedpaths = insertedpaths {
self.collectionView.insertItemsAtIndexPaths(theInsertedpaths)
}
if let theChangedpaths = changedpaths{
self.collectionView.reloadItemsAtIndexPaths(theChangedpaths)
}
if (collectionChanges.hasMoves) {
collectionChanges.enumerateMovesWithBlock() { fromIndex,toIndex in
let fromIndexPath = NSIndexPath(forItem: fromIndex,inSection: 0)
let toIndexPath = NSIndexPath(forItem: toIndex,inSection: 0)
self.collectionView.moveItemAtIndexPath(fromIndexPath,toIndexPath: toIndexPath)
}
}
},completion: nil)
}
} else {
// Detailed change information is not available;
// repopulate the UI from the current fetch result.
self.collectionView.reloadData()
}
}
}
}
func indexPathsFromIndexSetWithSection(indexSet:NSIndexSet?,section:Int) -> [NSIndexPath]?{
if indexSet == nil{
return nil
}
var indexPaths:[NSIndexPath] = []
indexSet?.enumerateIndexesUsingBlock { (index,Bool) -> Void in
indexPaths.append(NSIndexPath(forItem: index,inSection: section))
}
return indexPaths
}
Swift 3 / iOS 10版本:
func photoLibraryDidChange(_ changeInstance: PHChange) {
guard let collectionView = self.collectionView else {
return
}
// Photos may call this method on a background queue;
// switch to the main queue to update the UI.
dispatchQueue.main.async {
guard let fetchResults = self.fetchResults else {
collectionView.reloadData()
return
}
// Check for changes to the list of assets (insertions,or updates).
if let collectionChanges = changeInstance.changeDetails(for: fetchResults) {
// Get the new fetch result for future change tracking.
self.fetchResults = collectionChanges.fetchResultAfterChanges
if collectionChanges.hasIncrementalChanges {
// Get the changes as lists of index paths for updating the UI.
var removedpaths: [IndexPath]?
var insertedpaths: [IndexPath]?
var changedpaths: [IndexPath]?
if let removed = collectionChanges.removedindexes {
removedpaths = self.indexPaths(from: removed,section: 0)
}
if let inserted = collectionChanges.insertedindexes {
insertedpaths = self.indexPaths(from:inserted,section: 0)
}
if let changed = collectionChanges.changedindexes {
changedpaths = self.indexPaths(from: changed,section: 0)
}
var shouldReload = false
if let removedpaths = removedpaths,let changedpaths = changedpaths {
for changedpath in changedpaths {
if removedpaths.contains(changedpath) {
shouldReload = true
break
}
}
}
if let item = removedpaths?.last?.item {
if item >= fetchResults.count {
shouldReload = true
}
}
if shouldReload {
collectionView.reloadData()
} else {
// Tell the collection view to animate insertions/deletions/moves
// and to refresh any cells that have changed content.
collectionView.performBatchUpdates({
if let theRemovedpaths = removedpaths {
collectionView.deleteItems(at: theRemovedpaths)
}
if let theInsertedpaths = insertedpaths {
collectionView.insertItems(at: theInsertedpaths)
}
if let theChangedpaths = changedpaths {
collectionView.reloadItems(at: theChangedpaths)
}
collectionChanges.enumerateMoves { fromIndex,toIndex in
collectionView.moveItem(at: IndexPath(item: fromIndex,section: 0),to: IndexPath(item: toIndex,section: 0))
}
})
}
} else {
// Detailed change information is not available;
// repopulate the UI from the current fetch result.
collectionView.reloadData()
}
}
}
}
func indexPaths(from indexSet: IndexSet?,section: Int) -> [IndexPath]? {
guard let set = indexSet else {
return nil
}
return set.map { (index) -> IndexPath in
return IndexPath(item: index,section: section)
}
}