![](/img/trans.png)
[英]ALAssetsGroup enumerateAssetsUsingBlock: not returning assets
[英]ALAssetLibrary Notification and enumerateAssetsUsingBlock
我正在构建一个iOS应用程序,该应用程序允许用户拍摄照片并将其保存到自定义相册中,然后使用该相册中的照片填充该应用程序中的UICollectionView。 我在网络上找到了许多有关如何执行此操作的示例,但是我无法克服一个棘手的问题,因为最近的照片没有显示在相册中。 我结束了围绕使用通知和串行调度队列的工作,但是我认为它可以改进。
这是我保存图片然后重新填充UICollectionView的方法:
当需要枚举相册(viewDidLoad,并且收到ALAssetsLibraryChangedNotification)后,我构建了一个调用方法。
-(void)setupCollectionView {
_assets = [@[] mutableCopy];
__block NSMutableArray *tmpAssets = [@[] mutableCopy];
// This grabs a static instance of the ALAssetsLibrary
ALAssetsLibrary *assetsLibrary = [ProfileViewController defaultAssetsLibrary];
// Enumerate through all of the ALAssets (photos) in the user’s Asset Groups (Folders)
[assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if ([[group valueForProperty:ALAssetsGroupPropertyName] isEqualToString:albumName]) {
// Make sure we have the group... (this may be redundant)
if (group == nil){
return;
}
// I read in some SO posts that setting the filter might help, but I don't think it does
NSLog(@"Refresh pictures - %d",[group numberOfAssets]);
[group setAssetsFilter:[ALAssetsFilter allPhotos]];//this will cause group to reload
if (group!=nil) {
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if(result){
[tmpAssets addObject:result];
}else{
*stop = YES;
}
}];
}
self.assets = tmpAssets;
// Reload the UICollectionView
dispatch_async(dispatch_get_main_queue(), ^{
[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];
});
}
} failureBlock:^(NSError *error) {
NSLog(@"Error loading images %@", error);
}];
}
最初,这似乎工作得很好,但是有时,在将图像写入自定义相册后调用此功能时,刚拍摄的照片不会出现在收藏夹视图中。 照片将被保存到图库(显示在“照片”应用程序中),并且在退出并重新运行该应用程序后,将显示该照片,但枚举将不包括该最新照片。
正如一些关于此问题的Stack Overflow帖子所建议的那样,我尝试通过侦听ALAssetsLibraryChangedNotification来实现ALAssets的通知,但是我遇到了一些问题。 收到通知后,我正在做的事情是:
- (void) handleAssetChangedNotifiation:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
NSSet *updateAssets = userInfo[ALAssetLibraryUpdatedAssetsKey];
if (updateAssets.count>0) {
dispatch_sync(photoLoadQueue, ^{
[self setupCollectionView];
});
}
}
在我看来,这很好。 我正在检查ALAssetLibraryUpdatedAssetsKey中是否有任何数据,然后将其扔到调度队列中,一旦发现接收到保存单张照片的多个通知,便开始使用该队列。 这是我将照片写到自定义库后收到的通知:
Received notification: NSConcreteNotification 0x15ed4af0 {name = ALAssetsLibraryChangedNotification; object = <ALAssetsLibrary: 0x15db9e80>; userInfo = {
ALAssetLibraryUpdatedAssetGroupsKey = "{(\n assets-library://group/?id=43E80EF8-EA91-4C71-AFD2-EBDCB53A1D53\n)}";
ALAssetLibraryUpdatedAssetsKey = "{(\n assets-library://asset/asset.JPG?id=63E43AF3-3729-43E9-806C-BE463116FDA2&ext=JPG,\n assets-library://asset/asset.JPG?id=FA2ED24E-DF0F-49A3-85B8-9C181E4077A4&ext=JPG,\n assets-library://asset/asset.JPG?id=A0B66E2E-6EA1-462C-AD93-DB3087BA65D8&ext=JPG\n)}";}}
Received notification: NSConcreteNotification 0x15d538c0 {name = ALAssetsLibraryChangedNotification; object = <ALAssetsLibrary: 0x15db9e80>; userInfo = {
ALAssetLibraryUpdatedAssetsKey = "{(\n assets-library://asset/asset.JPG?id=FBDD2086-EC76-473F-A23E-4F4200C0A6DF&ext=JPG\n)}";}}
Received notification: NSConcreteNotification 0x15dc9230 {name = ALAssetsLibraryChangedNotification; object = <ALAssetsLibrary: 0x15db9e80>; userInfo = {}}
Received notification: NSConcreteNotification 0x15df5b40 {name = ALAssetsLibraryChangedNotification; object = <ALAssetsLibrary: 0x15db9e80>; userInfo = {}}
Received notification: NSConcreteNotification 0x15d6a050 {name = ALAssetsLibraryChangedNotification; object = <ALAssetsLibrary: 0x15db9e80>; userInfo = {}}
Received notification: NSConcreteNotification 0x15d379e0 {name = ALAssetsLibraryChangedNotification; object = <ALAssetsLibrary: 0x15db9e80>; userInfo = {
ALAssetLibraryUpdatedAssetGroupsKey = "{(\n assets-library://group/?id=1E76AFA7-89B4-4277-A175-D7C8E62E49D0&filter=1,\n assets-library://group/?id=1E76AFA7-89B4-4277-A175-D7C8E62E49D0\n)}";
ALAssetLibraryUpdatedAssetsKey = "{(\n assets-library://asset/asset.JPG?id=FBDD2086-EC76-473F-A23E-4F4200C0A6DF&ext=JPG\n)}";
Received notification: NSConcreteNotification 0x15df7bf0 {name = ALAssetsLibraryChangedNotification; object = <ALAssetsLibrary: 0x15db9e80>; userInfo = {
ALAssetLibraryUpdatedAssetGroupsKey = "{(\n assets-library://group/?id=8E390051-E107-42BC-AE55-24BA35966642\n)}";}}
Received notification: NSConcreteNotification 0x15d40360 {name = ALAssetsLibraryChangedNotification; object = <ALAssetsLibrary: 0x15db9e80>; userInfo = {
ALAssetLibraryUpdatedAssetsKey = "{(\n assets-library://asset/asset.JPG?id=5A45779A-C3C1-4545-9BD6-88F094FFA5B9&ext=JPG\n)}";}}
为什么我收到这么多通知? 我没有找到一种方法来查找一条特定的消息,该消息会在图像准备好后告诉我,因此我使用该分派队列顺序触发了枚举。 是否有人对此有解决方案的经验? 我的工作,但我肯定比我应该做的工作更多。
只是为了完整起见,这是我保存图像的方法(我可能不应该像我一样嵌套这些块,但是我这样做是为了尝试修复未检测到的添加图像):
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *originalImage, *editedImage, *imageToSave;
editedImage = (UIImage *) [info objectForKey:UIImagePickerControllerEditedImage];
originalImage = (UIImage *) [info objectForKey:UIImagePickerControllerOriginalImage];
if (editedImage) {
imageToSave = editedImage;
}else {
imageToSave = originalImage;
}
// Get group/asset library/album
__block ALAssetsGroup* groupToAddTo;
ALAssetsLibrary *assetsLibrary = [ProfileViewController defaultAssetsLibrary];
[[NSNotificationCenter defaultCenter] removeObserver:self name:ALAssetsLibraryChangedNotification object:nil];
[assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if ([[group valueForProperty:ALAssetsGroupPropertyName] isEqualToString:albumName]) {
NSLog(@"found album %@", albumName);
groupToAddTo = group;
// save to album
CGImageRef img = [imageToSave CGImage];
// There's some orientation stuff here I pulled out for brevity's sake
[assetsLibrary writeImageToSavedPhotosAlbum:img orientation: alOrientation completionBlock:^(NSURL* assetURL, NSError* error) {
if (error.code == 0) {
[assetsLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) {
// assign the photo to the album
if ([groupToAddTo valueForProperty:ALAssetsGroupPropertyURL] == nil){
NSLog(@"group properties are nil!");
}else {
if([groupToAddTo addAsset:asset]){
// If the asset writes to the library, listen for the notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAssetChangedNotifiation:) name:ALAssetsLibraryChangedNotification object:nil];
}else {
NSLog(@"Image Not Added!");
}
}
}
failureBlock:^(NSError* error) {
NSLog(@"failed to retrieve image asset:\nError: %@ ", [error localizedDescription]);
}];
}else {
NSLog(@"saved image failed.\nerror code %i\n%@", error.code, [error localizedDescription]);
}
}];
}}
failureBlock:^(NSError* error) {
NSLog(@"failed to enumerate albums:\nError: %@", [error localizedDescription]);}];
[picker dismissViewControllerAnimated:YES completion:NULL];
}
要实现的是与ALAssetsLibrary关联的块是异步的 。 因此,这样的代码不起作用:
if (group!=nil) {
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if(result){
[tmpAssets addObject:result];
}else{
*stop = YES;
}
}];
}
self.assets = tmpAssets;
“最后”一行self.assets = tmpAssets
在块之前运行-因此tmpAssets
永远没有机会进行正确设置。
对于与ALAssetsLibrary调用关联的所有块,也是如此。
一旦掌握了这一点,就可以重组代码,以便一切按正确的顺序进行。 实际上,事实证明这就是通过枚举块的额外处理。 当我在书中写道:
最初,我对这种奇怪的块枚举行为感到迷惑不解,但是有一天它突然出现了我的原因:这些块都被异步调用(在主线程上),这意味着其余代码已经完成运行,所以你给过块额外的一个阶段,以做一些与所有你可能聚集在前面的传递数据的第一次机会。
因此,您看到了,您希望代码的结构更像这样:
if (group!=nil) {
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if(result){
[tmpAssets addObject:result];
}else{
self.assets = tmpAssets; // <--- !!!!
// and now proceed to whatever the _next_ step is...!
}
}];
}
因此,您看到,您完全走错了路。 摆脱掉所有通知,并根据新知识完全重组所有代码。 一旦您了解了有关其工作原理的一个简单但文献记载不多的事实,ALAssetsLibrary就完全一致了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.