照片 API 将 PHFetchResultChangeDetails 索引转换为 NSMutableOrderedSet

Photos API convert PHFetchResultChangeDetails indexes to NSMutableOrderedSet

我在 PHChange 事件发生后尝试更改数据源时遇到问题。我使用 NSMutableOrderedSet 作为我的数据源类型。当 PHChange 发生时,它可以返回三个可能的索引:removedIndexesinsertedIndexeschangedIndexes.

问题是我不确定如何在先前的数据源中反映这些变化。有人可以阐明我如何转换 PHChange 中给出的索引来更新我的 recentsCollectionDataSource。我的 UICollectionView 使用 recentsCollectionDataSource 作为其数据源。

现在我认为唯一的方法是手动枚举和构建我自己的更改索引,而不是使用 PHFetchResultChangeDetails 索引。

如果能提供任何帮助,我将不胜感激。也欢迎任何关于制作我的数据源的替代方法的建议。

代码:

@property (nonatomic, strong) PHFetchResult *assetsFetchResult;
@property (nonatomic, strong) NSMutableOrderedSet *recentsCollectionDataSource;

- (void)setup
{
    self.recentsCollectionDataSource = [[NSMutableOrderedSet alloc]init];

    self.assetsFetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum | PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAny options:nil];

    for (PHAssetCollection *sub in self.assetsFetchResult)
    {
        PHFetchResult *assetsInCollection = [PHAsset fetchAssetsInAssetCollection:sub options:nil];

        for (PHAsset *asset in assetsInCollection)
        {
            [self.recentsCollectionDataSource addObject:asset];
        }
    }

    if (self.recentsCollectionDataSource.count > 0)
    {
        NSArray *array = [self.recentsCollectionDataSource sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]]];

        self.recentsCollectionDataSource = [[NSMutableOrderedSet alloc]initWithArray:array];
    }
}

- (void)photoLibraryDidChange:(PHChange *)changeInstance
{
    PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.assetsFetchResult];

    if (collectionChanges)
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^
        {
            self.assetsFetchResult = [collectionChanges fetchResultAfterChanges];

            if (!collectionChanges.hasIncrementalChanges || collectionChanges.hasMoves)
            {
                dispatch_async(dispatch_get_main_queue(),^
                {
                    [self.recentsCollectionView reloadData];
                });
            }
            else
            {
                dispatch_async(dispatch_get_main_queue(),^
                {
                    [self.recentsCollectionView performBatchUpdates:^
                     {
                         NSIndexSet *removedIndexes = [collectionChanges removedIndexes];
                         NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes];
                         NSIndexSet *changedIndexes = [collectionChanges changedIndexes];

                         // Need to update our old recentsCollectionDataSource
                         // somehow, otherwise we get a crash.

                         if (removedIndexes.count)
                         {
                             [self.recentsCollectionView deleteItemsAtIndexPaths:[removedIndexes indexPathsFromIndexesWithSection:0]];
                         }

                         if (insertedIndexes.count)
                         {
                             [self.recentsCollectionView insertItemsAtIndexPaths:[insertedIndexes indexPathsFromIndexesWithSection:0]];
                         }

                         if (changedIndexes.count)
                         {
                             [self.recentsCollectionView reloadItemsAtIndexPaths:[changedIndexes indexPathsFromIndexesWithSection:0]];
                         }
                     }completion:NULL];
                });
            }
        });
    }
}

@implementation NSIndexSet (Convenience)

- (NSArray *)indexPathsFromIndexesWithSection:(NSUInteger)section
{
    NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:self.count];

    [self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop)
    {
        [indexPaths addObject:[NSIndexPath indexPathForItem:idx inSection:section]];
    }];
    return indexPaths;
}

@end

我似乎已经解决了(至少现在)。我分别构建了自己的删除、插入和更改索引,这不如内置 PHFetchResultChangeDetails 索引有效,但我不相信有另一种方法可以做到这一点。使用此代码在 iPhone 5 上浏览 2600 张照片大约需要 4 秒,所以还不错。

我注意到 PHFetchResultChangeDetails 可能有一个错误,它没有将最喜欢的更改放在更改详细信息中,所以如果你想保持视觉效果,不妨再次执行整个集合提取在你的 UI.

中保持一致

代码:

- (void)photoLibraryDidChange:(PHChange *)changeInstance
{
    PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.assetsFetchResult];

    if (collectionChanges)
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^
        {
            self.assetsFetchResult = [collectionChanges fetchResultAfterChanges];

            NSMutableOrderedSet *newSet = [[NSMutableOrderedSet alloc]init];

            for (PHAssetCollection *sub in self.assetsFetchResult)
            {
                PHFetchResult *assetsInCollection = [PHAsset fetchAssetsInAssetCollection:sub options:nil];

                for (PHAsset *asset in assetsInCollection)
                {
                    [newSet addObject:asset];
                }
            }

            NSMutableIndexSet *removedIndexes = [[NSMutableIndexSet alloc]init];
            NSMutableIndexSet *insertedIndexes = [[NSMutableIndexSet alloc]init];
            NSMutableIndexSet *changedIndexes = [[NSMutableIndexSet alloc]init];

            NSMutableArray *objectsToInsert = [[NSMutableArray alloc]init];

            if (newSet.count > 0)
            {
                NSArray *array = [newSet sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]]];

                newSet = [[NSMutableOrderedSet alloc]initWithArray:array];

                for (id obj in newSet)
                {
                    NSUInteger idx = [self.recentsCollectionDataSource indexOfObject:obj];

                    if (idx == NSNotFound)
                    {
                        [objectsToInsert addObject:obj];

                        [insertedIndexes addIndex:[newSet indexOfObject:obj]];
                    }
                    else
                    {
                        NSUInteger objIdx = [newSet indexOfObject:obj];

                        PHAsset *oldAssetToCompare = (PHAsset *)[self.recentsCollectionDataSource objectAtIndex:idx];
                        PHAsset *plausibleNewAssetToCompare = (PHAsset *)[newSet objectAtIndex:objIdx];

                        if (![oldAssetToCompare isEqual:plausibleNewAssetToCompare])
                        {
                            [changedIndexes addIndex:idx];
                        }
                    }
                }

                for (id obj in self.recentsCollectionDataSource)
                {
                    NSUInteger idx = [newSet indexOfObject:obj];

                    if (idx == NSNotFound)
                    {
                        [removedIndexes addIndex:[self.recentsCollectionDataSource indexOfObject:obj]];
                    }
                    else
                    {
                        NSUInteger objIdx = [self.recentsCollectionDataSource indexOfObject:obj];

                        PHAsset *oldAssetToCompare = (PHAsset *)obj;
                        PHAsset *plausibleNewAssetToCompare = (PHAsset *)[newSet objectAtIndex:idx];

                        if (![oldAssetToCompare isEqual:plausibleNewAssetToCompare])
                        {
                            [changedIndexes addIndex:objIdx];
                        }
                    }
                }
            }

            if (removedIndexes.count > 0)
            {
                [self.recentsCollectionDataSource removeObjectsAtIndexes:removedIndexes];
            }

            if (insertedIndexes.count > 0)
            {
                [self.recentsCollectionDataSource insertObjects:objectsToInsert atIndexes:insertedIndexes];
            }

            if (!collectionChanges.hasIncrementalChanges || collectionChanges.hasMoves)
            {
                dispatch_async(dispatch_get_main_queue(),^
                {
                    [self.recentsCollectionView reloadData];
                });
            }
            else
            {
                dispatch_async(dispatch_get_main_queue(),^
                {
                    [self.recentsCollectionView performBatchUpdates:^
                     {
                         if (removedIndexes.count > 0)
                         {
                             [self.recentsCollectionView deleteItemsAtIndexPaths:[removedIndexes indexPathsFromIndexesWithSection:0]];
                         }

                         if (insertedIndexes.count > 0)
                         {
                             [self.recentsCollectionView insertItemsAtIndexPaths:[insertedIndexes indexPathsFromIndexesWithSection:0]];
                         }

                         if (changedIndexes.count > 0)
                         {
                             [self.recentsCollectionView reloadItemsAtIndexPaths:[changedIndexes indexPathsFromIndexesWithSection:0]];
                         }
                     }completion:NULL];
                });
            }
        });
    }
}