web-dev-qa-db-ja.com

iOS8で壊れたAssetsLibraryフレームワーク

IOS8でバグのように見えるアセットライブラリフレームワークの問題がiOS8で発生しました。「MyMedia」というアルバムを作成してから削除すると、アルバムを再度作成しようとすると、このチャンクが発生します。以下のコードは、「写真」アプリを使用してアルバムを削除したために存在しないにもかかわらず、アルバム「MyMedia」が存在することを示す「nil」を返します。

__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 Photosで、写真を保存したカスタムアルバムを削除します3.アプリをインストールします4.もしアプリで写真を撮ったりビデオを録画したりしても、作成したり保存したりすることはありません。 iOSフォトアルバムの下を見ると、カスタムアルバムは存在せず、アプリで撮影された写真/ビデオは存在しません。

19
Adam Freeman

私の以前の答えは間違っていました。私は実際にそれをテストしていませんでした。私はついに何をしなければならないかを理解しました、そしてそれは困難でした、しかし私はそれを働かせました。これは、iOS7.x.XとiOS8.X.xの両方でアプリを実行し、以前にアプリによって削除されたカスタムアルバムを作成するために私がしなければならなかったことです->

  1. 私は2つのコードチャンクを作成しました。1つはiOS8.x.xでPhotosフレームワークを使用し、もう1つはiOS7.x.xでAssetsLibraryフレームワークを使用します。

  2. Spアプリは両方のiOSバージョンで実行できます。アプリをフォトフレームワークにリンクしましたが、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);
   }];
}
11
Adam Freeman

アダムの答えと、ALAssetsLibraryのMarin Todorovのカテゴリ、ALAssetsLibrary + CustomPhotoAlbumを使用してフォトアルバムを作成し、写真を配置します。以下のコードは、そのカテゴリのメインのworkHorseを置き換え、iOS7デバイスとiOS8.1デバイスの両方で機能します。両方を持つこと。

不明なクラスのperformSelectorについて2つの警告が表示されますが、改善があれば幸いです。

(作成しなかった共有アルバムから写真をコピーすることはなく、メッセージで失敗します。そこでの機能強化も良いでしょう)

1)「オプション」に設定された「写真」フレームワークを追加します

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];
}
3
hokkuk

IOS7およびiOS8用のアルバムを作成するための以下の方法を試すことができます

#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");
    }];
}}
2
Gaurav

もっと早く更新すべきだったみんなを更新したかったのですが、仕事に忙殺されました。この問題はiOS8の問題ですが、iOS 8.0.2で修正されているため、修正するために必要なのはiOSをiOS8.0.2に更新することだけです。

1
Adam Freeman

以下のコードを使用して、特定のアルバムが存在するかどうかを確認し、存在しない場合は作成して、いくつかの画像を追加しました。 UIImageからアセットを作成した後、そのプレースホルダーを使用して、ブロックを離れることなくアルバムにアセットを追加します。

//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
Shaked Sayag

上記の提案はどれも役に立たなかったので、これが私がアセット(写真)をカスタムアルバム名に保存する際の問題を解決する方法でした。このコード:「fetchCollectionResult.count == 0」は、カスタムアルバムを一度削除して再度保存しようとした場合の状況を具体的に処理します。これは、fetchCollectionResultが「nil」でなくなる可能性があるためです。これを簡単に変更して、ビデオ/ムービーの保存もサポートできます。

このコードはiOS8専用です!デバイスが以前のバージョンで実行されている場合は、呼び出さないように注意する必要があります。

#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);
     }];
}
0
gStation