首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ALAssetLibrary通知和enumerateAssetsUsingBlock

ALAssetLibrary通知和enumerateAssetsUsingBlock
EN

Stack Overflow用户
提问于 2014-04-30 01:56:43
回答 1查看 1.3K关注 0票数 0

我正在构建一个iOS应用程序,允许用户拍摄一张照片并将其保存到一个自定义相册中,该相册用相册中的照片填充应用程序中的UICollectionView。我在网上找到了很多关于如何做到这一点的例子,但是我无法克服最近的照片没有出现在专辑中的一个棘手的问题。我结束了使用通知和串行调度队列构建工作,但我认为可以对其进行改进。

下面是我如何保存图片,然后重新填充UICollectionView:

当我需要枚举相册时,我构建了一个调用方法(viewDidLoad,以及一旦收到ALAssetsLibraryChangedNotification)。

代码语言:javascript
复制
-(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);
    }];
}

最初,它似乎工作得很好,但偶尔,在将图像写入自定义相册后调用它时,刚刚拍摄的照片不会出现在集合视图中。照片将保存到库中(它显示在Photos应用程序中),在退出并重新运行应用程序之后,照片将显示出来,但是枚举将不包括最新的照片。

正如一些Stack溢出帖子所建议的,我尝试通过侦听ALAssetsLibraryChangedNotification来实现ALAssetsLibraryChangedNotification通知,但我对此有一些问题。下面是我收到通知后正在做的事情:

代码语言:javascript
复制
- (void) handleAssetChangedNotifiation:(NSNotification *)notification {
    NSDictionary *userInfo = notification.userInfo;
    NSSet *updateAssets = userInfo[ALAssetLibraryUpdatedAssetsKey];
    if (updateAssets.count>0) {        
        dispatch_sync(photoLoadQueue, ^{
            [self setupCollectionView];
        });
    }
}

这对我来说很好。我正在检查ALAssetLibraryUpdatedAssetsKey中是否有任何数据,然后将其抛到调度队列中,一旦发现我收到了几个保存一张照片的通知,就开始使用它。以下是当我将照片写入自定义库时收到的通知:

代码语言:javascript
复制
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)}";}}

为什么我收到这么多通知?我看不出有一种方法可以查找一条特定的消息,一旦图像准备好,它就会告诉我,所以我使用这个分派队列来连续地触发枚举。有没有人有这方面的经验或解决方案?我的工作很好,但我的工作肯定比我应该做的要多。

为了完整起见,下面是保存映像的方法(我可能不应该像以前那样嵌套这些块,但我这样做是为了修复未被检测到的添加的图像):

代码语言:javascript
复制
- (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];
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-04-30 02:05:08

需要认识到的是,与ALAssetsLibrary相关的块是异步的。因此,这样的代码不起作用:

代码语言:javascript
复制
        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调用相关的所有块也是如此。

一旦您掌握了这一点,您就可以重新构造代码,以便一切都按照正确的顺序进行。实际上,这就是通过枚举块的额外传递的目的。当我在我的书中写道:

最初,我对这种奇怪的块枚举行为感到困惑,但有一天它突然出现在我的脑海中:这些块都是异步调用的(在主线程上),这意味着您的其余代码已经完成了运行,因此您第一次有机会对您在前面几次代码中收集到的所有数据进行处理时,会有一个额外的遍历块。

因此,您可以看到,您希望代码的结构更像这样:

代码语言:javascript
复制
        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是完全连贯的,一旦您了解了这个简单但文档不足的事实,它是如何工作的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23378377

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档