iOS 8上的AssetsLibrary框架失效

19

我在iOS 8上使用Assets Library框架遇到了一个问题,这似乎是iOS 8中的一个bug。如果我创建一个名为'MyMedia'的相册,然后将其删除,当我尝试再次创建该相册时,下面的代码块将返回'nil',表明相册'MyMedia'存在,尽管我已经使用'Photos'应用程序将其删除。

__block ALAssetsGroup *myGroup = nil;
__block BOOL addAssetDone = false;
NSString *albumName = @"MyMedia";
[assetsLib addAssetsGroupAlbumWithName:albumName
                           resultBlock:^(ALAssetsGroup *group) {
                               myGroup = group;
                               addAssetDone = true;
                           } failureBlock:^(NSError *error) {
                               NSLog( @"failed to create album: %@", albumName);
                               addAssetDone = true;
                           }];

while (!addAssetDone) {
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.05f]];
}
return myGroup; // returns nil if group has previously been created and then deleted

同样的方法对于创建新相册'MyMedia2'也有效。是否有其他人遇到过这个问题并知道解决方法?是移动到新的“照片”框架,还是我在这里做错了什么?请注意,此代码始终在iOS7.X上运行。

实际上,重新制造此问题的步骤如下 -> 1. 卸载拍摄照片并将其保存到自定义相册的应用程序 2. 在iOS照片下删除已保存照片的自定义相册 3. 安装您的应用程序 4. 如果使用该应用程序拍摄照片或录制视频,则不会创建或存储它们。如果查看iOS照片相册,自定义的相册不存在,并且使用应用程序拍摄的任何图片/视频都不存在。


你可能想要开始编写与照片框架相关的代码。我只能说,这真是太麻烦了... - Paul Cezanne
如果我使用Xcode 6和Photos框架构建应用程序,我仍然可以在安装了7.X的设备上运行它吗? - Adam Freeman
1
不,它只支持iOS8。而且是的,这让事情变得困难,基本上你必须使用两个API进行编码。 - Paul Cezanne
遇到了同样的问题,真糟糕! - Kjuly
同样的问题?我能删除资产吗,还是我永远无法重新创建一个具有相同名称的相册? - oarfish
你不能使用“资产库”框架删除该资产,因为出于某些原因,苹果不允许这样做(只有照片应用程序才允许使用“资产库”框架删除资产)。但是,你可以使用“照片”框架删除该资产。你可以在iCloud上创建多个完全相同的相册名称。 - Adam Freeman
6个回答

11

我的先前答案是不正确的。我当时没有真正测试过它。后来我终于弄清楚了需要做什么,虽然很困难但我还是解决了。以下是我为了让我的应用程序在iOS 7.x.X和iOS 8.X.x上运行并创建一个之前被应用程序删除的自定义相册所做的工作:

  1. 我编写了两个代码块:一个使用iOS 8.x.x上的Photos框架,另一个使用iOS 7.x.x上的AssetsLibrary框架。

  2. 为了使应用程序能够在两个iOS版本上运行,我将应用程序链接到Photos框架,但将其从必需更改为可选,因此它不会在iOS 7.x.x上加载。

  3. 由于在iOS 7.x.x上无法直接调用Photos框架代码,因此我不得不更改它以便在运行时动态加载类、函数(和块)。

以下是可以在iPhone上运行的代码块。这也应该在模拟器中运行 -->

// PHPhotoLibrary_class will only be non-nil on iOS 8.x.x
Class PHPhotoLibrary_class = NSClassFromString(@"PHPhotoLibrary");

if (PHPhotoLibrary_class) {

   /**
    *
    iOS 8..x. . code that has to be called dynamically at runtime and will not link on iOS 7.x.x ...

    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
    } completionHandler:^(BOOL success, NSError *error) {
        if (!success) {
            NSLog(@"Error creating album: %@", error);
        }
    }];
    */

    // dynamic runtime code for code chunk listed above            
    id sharedPhotoLibrary = [PHPhotoLibrary_class performSelector:NSSelectorFromString(@"sharedPhotoLibrary")];

    SEL performChanges = NSSelectorFromString(@"performChanges:completionHandler:");

    NSMethodSignature *methodSig = [sharedPhotoLibrary methodSignatureForSelector:performChanges];

    NSInvocation* inv = [NSInvocation invocationWithMethodSignature:methodSig];
    [inv setTarget:sharedPhotoLibrary];
    [inv setSelector:performChanges];

    void(^firstBlock)() = ^void() {
        Class PHAssetCollectionChangeRequest_class = NSClassFromString(@"PHAssetCollectionChangeRequest");
        SEL creationRequestForAssetCollectionWithTitle = NSSelectorFromString(@"creationRequestForAssetCollectionWithTitle:");
        [PHAssetCollectionChangeRequest_class performSelector:creationRequestForAssetCollectionWithTitle withObject:albumName];

    };

    void (^secondBlock)(BOOL success, NSError *error) = ^void(BOOL success, NSError *error) {
       if (success) {
           [assetsLib enumerateGroupsWithTypes:ALAssetsGroupAlbum usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
               if (group) {
                   NSString *name = [group valueForProperty:ALAssetsGroupPropertyName];
                   if ([albumName isEqualToString:name]) {
                       groupFound = true;
                       handler(group, nil);
                   }
               }
           } failureBlock:^(NSError *error) {
               handler(nil, error);
           }];
       }

       if (error) {
           NSLog(@"Error creating album: %@", error);
           handler(nil, error);
       }
   };

   // Set the success and failure blocks.
   [inv setArgument:&firstBlock atIndex:2];
   [inv setArgument:&secondBlock atIndex:3];

   [inv invoke];

}
else {   
   // code that always creates an album on iOS 7.x.x but fails
   // in certain situations such as if album has been deleted
   // previously on iOS 8...x. .              
   [assetsLib addAssetsGroupAlbumWithName:albumName
       resultBlock:^(ALAssetsGroup *group) {
       handler(group, nil);
   } failureBlock:^(NSError *error) {
       NSLog( @"Failed to create album: %@", albumName);
       handler(nil, error);
   }];
}

@Kjuly:handler(group, nil); 给我声明错误,请给我声明代码。 - Chirag Pipaliya
2
@ChiragPipaliya 也许你可以尝试这个库:https://github.com/Kjuly/ALAssetsLibrary-CustomPhotoAlbum。Adam已经将其修复iOS 8的问题并合并了。 :) - Kjuly
1
我建议不要使用NSSelectorFromString,而是直接使用@selector({方法名称})。这样你就不会受到拼写错误的影响。让编译器来帮助你! - Brian Sachetta
@AdamFreeman 你有尝试在iOS7中按照时刻获取所有照片吗? 我已经获取到了所有的照片,但是无法获取相册创建日期和事件名称。我想开发一个类似于iPhoto应用程序中按照时刻显示所有数据的应用程序。在iOS8中,它可以显示所有时刻并且运行良好,但是在iOS7中使用PHAsset无法获取照片。 - Pratik Patel
在“firstBlock”中创建相册之前,有没有办法检查它是否已经存在?当我运行这段代码时,每次启动应用程序时都会创建相同的自定义相册(显然这是不希望的)。我只想说:如果相册不存在,则创建一个;否则,什么也不做。有什么解决方案吗? - Friendly King
显示剩余2条评论

3

使用Adam的回答和Marin Todorov的ALAssetsLibrary类别,ALAssetsLibrary+CustomPhotoAlbum来创建相册,并将照片放入其中,在此代码中,替换了该类别中的主要工作内容,对于需要同时支持iOS7设备和iOS8.1设备的人来说,它都能够工作。

尽管它会在未知类上执行performSelector时给出两个警告,但任何改进都将不胜感激:

(它不会复制您未创建的共享相册中的照片,并且会失败并显示消息。任何增强也将非常好)

1)添加“Photos”框架,设置为“可选”

2)包括导入行#import <Photos/PHPhotoLibrary.h>

    //----------------------------------------------------------------------------------------
- (void)addAssetURL:(NSURL *)assetURL
            toAlbum:(NSString *)albumName
         completion:(ALAssetsLibraryWriteImageCompletionBlock)completion
            failure:(ALAssetsLibraryAccessFailureBlock)failure
{
NSLog();
    __block BOOL albumWasFound = NO;

    //-----------------------------------------
    ALAssetsLibraryGroupsEnumerationResultsBlock enumerationBlock;
    enumerationBlock = ^(ALAssetsGroup *group, BOOL *stop)
    {
NSLog(@"  ALAssetsLibraryGroupsEnumerationResultsBlock");
        // Compare the names of the albums
        if ([albumName compare:[group valueForProperty:ALAssetsGroupPropertyName]] == NSOrderedSame)
        {
NSLog(@"--------------Target album is found");
            // Target album is found
            albumWasFound = YES;

            // Get a hold of the photo's asset instance
            // If the user denies access to the application, or if no application is allowed to
            //   access the data, the failure block is called.
            ALAssetsLibraryAssetForURLResultBlock assetForURLResultBlock =
            [self _assetForURLResultBlockWithGroup:group
                                          assetURL:assetURL
                                        completion:completion
                                           failure:failure];

            [self assetForURL:assetURL
                resultBlock:assetForURLResultBlock
               failureBlock:failure];

            // Album was found, bail out of the method
            *stop = YES;
        }

        if (group == nil && albumWasFound == NO)
        {
NSLog(@"--------------Target album does not exist");
            // Photo albums are over, target album does not exist, thus create it

            // Since you use the assets library inside the block,
            //   ARC will complain on compile time that there’s a retain cycle.
            //   When you have this – you just make a weak copy of your object.
            ALAssetsLibrary * __weak weakSelf = self;

            // If iOS version is lower than 5.0, throw a warning message
            if (! [self respondsToSelector:@selector(addAssetsGroupAlbumWithName:resultBlock:failureBlock:)])
            {
NSLog(@"--------------Target album does not exist and does not respond to addAssetsGroupAlbumWithName");
            } else {
NSLog(@"--------------Target album does not exist addAssetsGroupAlbumWithName");

                // -----------   PHPhotoLibrary_class will only be non-nil on iOS 8.x.x  -----------
                Class PHPhotoLibrary_class = NSClassFromString(@"PHPhotoLibrary");
NSLog(@"PHPhotoLibrary_class %@ ", PHPhotoLibrary_class);

                if (PHPhotoLibrary_class)
                {
NSLog(@"iOS8");

                    // ---------  dynamic runtime code  -----------
                    id sharedPhotoLibrary = [PHPhotoLibrary_class performSelector:NSSelectorFromString(@"sharedPhotoLibrary")];
NSLog(@"sharedPhotoLibrary %@ ", sharedPhotoLibrary);

                    SEL performChanges = NSSelectorFromString(@"performChanges:completionHandler:");

                    NSMethodSignature *methodSig = [sharedPhotoLibrary methodSignatureForSelector:performChanges];

                    NSInvocation* inv = [NSInvocation invocationWithMethodSignature:methodSig];
                    [inv setTarget:sharedPhotoLibrary];
                    [inv setSelector:performChanges];

                    void(^firstBlock)() = ^void()
                    {
NSLog(@"firstBlock");
                        Class PHAssetCollectionChangeRequest_class = NSClassFromString(@"PHAssetCollectionChangeRequest");
                        SEL creationRequestForAssetCollectionWithTitle = NSSelectorFromString(@"creationRequestForAssetCollectionWithTitle:");
NSLog(@"PHAssetCollectionChangeRequest_class %@ ", PHAssetCollectionChangeRequest_class);


                        [PHAssetCollectionChangeRequest_class performSelector:creationRequestForAssetCollectionWithTitle withObject:albumName];

                    };

                    void (^secondBlock)(BOOL success, NSError *error) = ^void(BOOL success, NSError *error)
                    {
NSLog(@"secondBlock");
                       if (success)
                       {
NSLog(@"success");
                            [self enumerateGroupsWithTypes:ALAssetsGroupAlbum usingBlock:^(ALAssetsGroup *group, BOOL *fullStop)
                            {
                               if (group)
                               {
NSLog(@"group %@ ", group);
                                   NSString *name = [group valueForProperty:ALAssetsGroupPropertyName];
                                   if ([albumName isEqualToString:name])
                                   {
NSLog(@"[albumName isEqualToString:name] %@ ", name);
                                        ALAssetsLibraryAssetForURLResultBlock assetForURLResultBlock =
                                        [self _assetForURLResultBlockWithGroup:group
                                                                      assetURL:assetURL
                                                                    completion:completion
                                                                       failure:failure];

                                        [self assetForURL:assetURL
                                            resultBlock:assetForURLResultBlock
                                           failureBlock:failure];

                                        *fullStop = YES;
                                   }
                               }
                            } failureBlock:failure];
                       }

                       if (error)
                       {
NSLog(@"Error creating album: %@", error);
                       }
                   };

                   // Set the success and failure blocks.
                   [inv setArgument:&firstBlock atIndex:2];
                   [inv setArgument:&secondBlock atIndex:3];

                   [inv invoke];

                } else {
NSLog(@"iOS7");
                    [self addAssetsGroupAlbumWithName:albumName resultBlock:^(ALAssetsGroup *createdGroup)
                    {
                        // Get the photo's instance, add the photo to the newly created album
                        ALAssetsLibraryAssetForURLResultBlock assetForURLResultBlock =
                            [weakSelf _assetForURLResultBlockWithGroup:createdGroup
                                                            assetURL:assetURL
                                                          completion:completion
                                                             failure:failure];

                        [weakSelf assetForURL:assetURL
                                  resultBlock:assetForURLResultBlock
                                 failureBlock:failure];
                    }
                    failureBlock:failure];
                }
            }
            // Should be the last iteration anyway, but just in case
            *stop = YES;
        }
    };



    // Search all photo albums in the library
    [self enumerateGroupsWithTypes:ALAssetsGroupAlbum
                  usingBlock:enumerationBlock
                failureBlock:failure];
}

2
您可以尝试以下方法来创建iOS 7和iOS 8的相册。
#define PHOTO_ALBUM_NAME @"AlbumName Videos"
#pragma mark - Create Album
-(void)createAlbum{

// PHPhotoLibrary_class will only be non-nil on iOS 8.x.x
Class PHPhotoLibrary_class = NSClassFromString(@"PHPhotoLibrary");

if (PHPhotoLibrary_class) {


    // iOS 8..x. . code that has to be called dynamically at runtime and will not link on iOS 7.x.x ...

    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:PHOTO_ALBUM_NAME];
    } completionHandler:^(BOOL success, NSError *error) {
        if (!success) {
            NSLog(@"Error creating album: %@", error);
        }else{
            NSLog(@"Created");
        }
    }];
}else{
    [self.library addAssetsGroupAlbumWithName:PHOTO_ALBUM_NAME resultBlock:^(ALAssetsGroup *group) {
        NSLog(@"adding album:'Compressed Videos', success: %s", group.editable ? "YES" : "NO");

        if (group.editable == NO) {
        }

    } failureBlock:^(NSError *error) {
        NSLog(@"error adding album");
    }];
}}

1
我使用以下代码来检查特定的相册是否存在,如果不存在,则创建该相册并向其中添加几张图片。从UIImage创建Asset后,我使用其占位符将其添加到相册中而不离开块。
//Will enter only in iOS 8+
Class PHPhotoLibrary_class = NSClassFromString(@"PHPhotoLibrary");

if (PHPhotoLibrary_class)
{
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^
    {
        //Checks for App Photo Album and creates it if it doesn't exist
        PHFetchOptions *fetchOptions = [PHFetchOptions new];
        fetchOptions.predicate = [NSPredicate predicateWithFormat:@"title == %@", kAppAlbumName];
        PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:fetchOptions];

        if (fetchResult.count == 0)
        {
            //Create Album
            PHAssetCollectionChangeRequest *albumRequest = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:kAppAlbumName];

            //Add default photos to it
            NSMutableArray *photoAssets = [[NSMutableArray alloc] init];

            for (UIImage *image in albumDefaultImages)
            {
                PHAssetChangeRequest *imageRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
                [photoAssets addObject:imageRequest.placeholderForCreatedAsset];
            }

            [albumRequest addAssets:photoAssets];
        }
    }
    completionHandler:^(BOOL success, NSError *error)
    {
        NSLog(@"Log here...");
    }];
}

1

我想更新一下,应该早点更新但是工作有点忙。这个问题与iOS 8有关,但已在iOS 8.0.2中得到解决,所以您只需要将iOS更新到iOS 8.0.2即可解决问题。


谢谢,伙计。我已经快被搞疯了。但是我在Xcode 6.0.1和iOS 8 iPhone 6模拟器上仍然遇到同样的运行问题。我该怎么解决呢? - Sam B
1
在8.3版本中也遇到了同样的问题,你也是吗? - Bhumit Mehta
我还没有在8.3上进行测试。你在8.3上看到了什么?经过更多的测试和使用不同的资产库框架函数后,唯一需要为8.0、8.0.1和8.0.2更改的代码是创建自定义相册。我不记得我使用的原始AL函数是什么,它不能正常工作,但我最终使用了其他一些可以正常工作的函数,解决了除创建自定义相册之外的所有其他问题。 - Adam Freeman

0
由于以上建议都没有帮助到我,下面是我解决保存资产(照片)到自定义相册名称的问题的方法。 这段代码:"fetchCollectionResult.count==0"特别处理了当您删除了自定义相册后,再次尝试保存到它时的情况,因为我猜测fetchCollectionResult可能不再是'nil'。 您可以很容易地修改它以支持保存视频/电影。
此代码仅适用于iOS 8! 如果设备运行较早版本,请务必不要调用它!
#define PHOTO_ALBUM_NAME @"MyPhotoAlbum"

NSString* existingAlbumIdentifier = nil;

-(void)saveAssetToAlbum:(UIImage*)myPhoto
{
    PHPhotoLibrary* photoLib = [PHPhotoLibrary sharedPhotoLibrary];

    __block NSString* albumIdentifier = existingAlbumIdentifier;
    __block PHAssetCollectionChangeRequest* collectionRequest;

    [photoLib performChanges:^
     {
         PHFetchResult* fetchCollectionResult;
         if ( albumIdentifier )
             fetchCollectionResult = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[albumIdentifier] options:nil];

         // Create a new album
         if ( !fetchCollectionResult || fetchCollectionResult.count==0 )
         {
             NSLog(@"Creating a new album.");
             collectionRequest = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:PHOTO_ALBUM_NAME];
             albumIdentifier = collectionRequest.placeholderForCreatedAssetCollection.localIdentifier;
         }
         // Use existing album
         else
         {
             NSLog(@"Fetching existing album, of #%d albums found.", fetchCollectionResult.count);
             PHAssetCollection* exisitingCollection = fetchCollectionResult.firstObject;
             collectionRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:exisitingCollection];
         }

         NSLog(@"Album local identifier = %@", albumIdentifier);

         PHAssetChangeRequest* createAssetRequest;
         createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:myPhoto];

         [collectionRequest addAssets:@[createAssetRequest.placeholderForCreatedAsset]];
     }
           completionHandler:^(BOOL success, NSError *error)
     {
         if (success)
         {
             existingAlbumIdentifier = albumIdentifier;
             NSLog(@"added image to album:%@", PHOTO_ALBUM_NAME);
         }
         else
             NSLog(@"Error adding image to  album: %@", error);
     }];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接