PhotoKit作为iOS8新推出的照片库,相比较于之前的ALAssetsLibrary确实解决了不少问题,那么结合我最近的使用,也借鉴了"TZImagePickerController"、"CTAssetsPickerController"等流行的开源框架,以及官方的SwiftDemo,来讲讲几个难以察觉的点。
内存问题
自定义相册基本思路都是拿UICollectionView来展示各种列表,那么内存主要就存在于对照片缩略图的获取以及滑动卡顿的问题,我们来看看TZImagePickerController,
__block UIImage *image;
// 修复获取图片时出现的瞬间内存过高问题
// 下面两行代码,来自hsjcom,他的github是:https://github.com/hsjcom 表示感谢
PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init];
option.resizeMode = PHImageRequestOptionsResizeModeFast;
int32_t imageRequestID = [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:imageSize contentMode:PHImageContentModeAspectFill options:option resultHandler:^(UIImage *result, NSDictionary *info) {
if (result) {
image = result;
}
BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]);
if (downloadFinined && result) {
result = [self fixOrientation:result];
if (completion) completion(result,info,[[info objectForKey:PHImageResultIsDegradedKey] boolValue]);
}
// Download image from iCloud / 从iCloud下载图片
if ([info objectForKey:PHImageResultIsInCloudKey] && !result && networkAccessAllowed) {
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
if (progressHandler) {
progressHandler(progress, error, stop, info);
}
});
};
options.networkAccessAllowed = YES;
options.resizeMode = PHImageRequestOptionsResizeModeFast;
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
UIImage *resultImage = [UIImage imageWithData:imageData scale:0.1];
resultImage = [self scaleImage:resultImage toSize:imageSize];
if (!resultImage) {
resultImage = image;
}
resultImage = [self fixOrientation:resultImage];
if (completion) completion(resultImage,info,NO);
}];
}
}];
注释有提到修复了瞬间内存过高,但测试发现iPad mini上快速滑动依然会有内存警告最终导致Crash。(CTAssetsPickerController同样如此)这里PHImageRequestOptionsResizeModeFast设置,会有两次结果回调,一次模糊,一次清晰(如果有的话),一般来说没什么问题。有说可以通过requestImageDataForAsset解决,没错这种方式内存占用确实要小不少,但Data到Image这一步需要做不少处理吧,并没有之前的API那么方便。我的结论是将contentMode改为PHImageContentModeAspectFit,同时尽量利用PHCachingImageManager来做缓存,这样效果会好很多。
iCloud问题
目前的系统设置有几个选项,针对用户开启了iCloud照片库,并且选择了“优化iPhone/iPad存储空间”或者选择了“下载并保留原件”但原件还未加载出来,也就是说资源不在本地。PHImageRequestOptions或者PHVideoRequestOptions在开启了PHVideoRequestOptions,会试图从iCloud去下载资源,这时候耗时可能会很长,另外也可能载入不成功,这两点都必须有严格的过度处理。这两点在之前的两个开源库中都有体现。而在我们开发中,大部分可能都忽视了这一点。
相册变更
这个主要是系统相册的增删,或者iCloud的更新对自定义相册的影响。原本是参考官方Swif版本来进行处理,没想到坑就在这里,同时更新和删除会Crash,官方Demo也不例外。主要是在performBatchUpdates这个地方。造成了线上好些崩溃记录。目前的处理如下
if (changes == nil) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
self.allVideos = changes.fetchResultAfterChanges;
UICollectionView *collectionView = self.collectionView;
if (!changes.hasIncrementalChanges || changes.hasMoves)
{
[collectionView reloadData];
[self fixupSelection];
[self resetCachedAssets];
}
else
{
NSArray *removedPaths;
NSArray *insertedPaths;
NSArray *changedPaths;
NSIndexSet *removedIndexes = changes.removedIndexes;
removedPaths = [removedIndexes vk_assetGridIndexPathsFromIndexesWithSection:0];
NSIndexSet *insertedIndexes = changes.insertedIndexes;
insertedPaths = [insertedIndexes vk_assetGridIndexPathsFromIndexesWithSection:0];
NSIndexSet *changedIndexes = changes.changedIndexes;
changedPaths = [changedIndexes vk_assetGridIndexPathsFromIndexesWithSection:0];
BOOL shouldReload = NO;
if (changedPaths != nil && removedPaths != nil)
{
for (NSIndexPath *changedPath in changedPaths)
{
if ([removedPaths containsObject:changedPath])
{
shouldReload = YES;
break;
}
}
}
if (removedPaths.lastObject && ((NSIndexPath *)removedPaths.lastObject).item >= self.allVideos.count)
{
shouldReload = YES;
}
if (shouldReload)
{
[collectionView reloadData];
[self fixupSelection];
}
else
{
[collectionView performBatchUpdates:^{
if (removedPaths.count)
{
[collectionView deleteItemsAtIndexPaths:[removedIndexes vk_assetGridIndexPathsFromIndexesWithSection:0]];
}
if (insertedPaths.count)
{
[collectionView insertItemsAtIndexPaths:[insertedIndexes vk_assetGridIndexPathsFromIndexesWithSection:0]];
}
if (changedPaths.count)
{
[collectionView reloadItemsAtIndexPaths:[changedIndexes vk_assetGridIndexPathsFromIndexesWithSection:0] ];
}
} completion:^(BOOL finished){
if (finished) {
[self resetCachedAssets];
[self fixupSelection];
}
}];
}
}
[self.emptyView setHidden:self.allVideos.count > 0];
});
视频封面截取帧
封面要求截取9张图片,之前有通过循环一次性截取9张图片,但这个操作有些耗时,看到系统有AVAssetImageGenerator,所以采用了这种方案。但这个API有一个问题,传入的Times数组是9个值,回调时并不一定给你回调9次,有可能更多,所以在回调时候最好做一些边界处理。
以上就是这次使用遇到的一系列问题。
网友评论