iOS 将照片保存在特定应用的相册中

Posted

技术标签:

【中文标题】iOS 将照片保存在特定应用的相册中【英文标题】:iOS save photo in an app specific album 【发布时间】:2012-08-15 15:20:51 【问题描述】:

我正在创建一个 ios 5 应用程序。我想将照片保存到设备中。

我想将照片保存到特定于我的应用的相册中,因此我需要创建相册,然后将照片保存到相册中。

我知道如何创建相册:

ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library addAssetsGroupAlbumWithName:@"MY APP NAME" resultBlock:^(ALAssetsGroup *group) 
    //How to get the album URL?
 failureBlock:^(NSError *error) 
    //Handle the error
];

我现在想将照片添加到新相册,我该怎么做?示例代码非常感谢!

【问题讨论】:

This article 似乎可以通过自定义 ALAssetsLibrary 类别实现您正在寻找的东西。看起来文章下面有一些有用的 cmets 也可能有助于提高性能。 【参考方案1】:

您可以使用以下代码更改专辑名称:

__weak ALAssetsLibrary *lib = self.library;

[self.library addAssetsGroupAlbumWithName:@"My Photo Album" resultBlock:^(ALAssetsGroup *group) 

    ///checks if group previously created
    if(group == nil)

        //enumerate albums
        [lib enumerateGroupsWithTypes:ALAssetsGroupAlbum
                           usingBlock:^(ALAssetsGroup *g, BOOL *stop)
         
             //if the album is equal to our album
             if ([[g valueForProperty:ALAssetsGroupPropertyName] isEqualToString:@"My Photo Album"]) 

                 //save image
                 [lib writeImageDataToSavedPhotosAlbum:UIImagePNGRepresentation(image) metadata:nil
                                       completionBlock:^(NSURL *assetURL, NSError *error) 

                                           //then get the image asseturl
                                           [lib assetForURL:assetURL
                                                resultBlock:^(ALAsset *asset) 
                                                    //put it into our album
                                                    [g addAsset:asset];
                                                 failureBlock:^(NSError *error) 

                                                ];
                                       ];

             
         failureBlock:^(NSError *error)

         ];

    else
        // save image directly to library
        [lib writeImageDataToSavedPhotosAlbum:UIImagePNGRepresentation(image) metadata:nil
                              completionBlock:^(NSURL *assetURL, NSError *error) 

                                  [lib assetForURL:assetURL
                                       resultBlock:^(ALAsset *asset) 

                                           [group addAsset:asset];

                                        failureBlock:^(NSError *error) 

                                       ];
                              ];
    

 failureBlock:^(NSError *error) 

];

【讨论】:

太好了,它的工作。我在声明 ALAssetsLibrary *lib 时删除了 __weak,因为 completionBlock 没有调用。【参考方案2】:

对于希望从 iOS 9 开始执行此操作的任何人,由于 ALAssetsLibrary 已被弃用,取而代之的是新的照片库,因此事情变得有些复杂。

这里有一些用于将 UIImages 添加到特定专辑名称的 Swift 代码(如果专辑不存在,则创建该专辑),您可能需要根据需要进行一些重构/优化:

func insertImage(image : UIImage, intoAlbumNamed albumName : String) 

    //Fetch a collection in the photos library that has the title "albumNmame"
    let collection = fetchAssetCollectionWithAlbumName(albumName)

    if collection == nil 
        //If we were unable to find a collection named "albumName" we'll create it before inserting the image
        phphotoLibrary.sharedPhotoLibrary().performChanges(
            PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle(albumName)
            , completionHandler: (success : Bool, error : NSError?) in
                if error != nil 
                    print("Error: " + error!.description)
                

                if success 
                    //Fetch the newly created collection (which we *assume* exists here)
                    let newCollection = self.fetchAssetCollectionWithAlbumName(albumName)
                    self.insertImage(image, intoAssetCollection: newCollection!)
                
            
        )
     else 
        //If we found the existing AssetCollection with the title "albumName", insert into it
        self.insertImage(image, intoAssetCollection: collection!)
    


func fetchAssetCollectionWithAlbumName(albumName : String) -> PHAssetCollection? 

    //Provide the predicate to match the title of the album.
    let fetchOption = PHFetchOptions()
    fetchOption.predicate = NSPredicate(format: "title == '" + albumName + "'")

    //Fetch the album using the fetch option
    let fetchResult = PHAssetCollection.fetchAssetCollectionsWithType(
        PHAssetCollectionType.Album,
        subtype: PHAssetCollectionSubtype.AlbumRegular,
        options: fetchOption)

    //Assuming the album exists and no album shares it's name, it should be the only result fetched
    let collection = fetchResult.firstObject as? PHAssetCollection

    return collection


func insertImage(image : UIImage, intoAssetCollection collection : PHAssetCollection) 

    //Changes for the Photos Library must be maded within the performChanges block
    PHPhotoLibrary.sharedPhotoLibrary().performChanges(

            //This will request a PHAsset be created for the UIImage
            let creationRequest = PHAssetCreationRequest.creationRequestForAssetFromImage(image)

            //Create a change request to insert the new PHAsset in the collection
            let request = PHAssetCollectionChangeRequest(forAssetCollection: collection)

            //Add the PHAsset placeholder into the creation request.
            //The placeholder is used because the actual PHAsset hasn't been created yet
            if request != nil && creationRequest.placeholderForCreatedAsset != nil 
                request!.addAssets([creationRequest.placeholderForCreatedAsset!])
            

        ,

        completionHandler:  (success : Bool, error : NSError?) in
            if error != nil 
                print("Error: " + error!.description)
            
        
    )

【讨论】:

【参考方案3】:

对于那些在 Objective-C. 中寻找 Eddy 答案的人

#import <Photos/Photos.h>

- (void)insertImage:(UIImage *)image intoAlbumNamed:(NSString *)albumName 
    //Fetch a collection in the photos library that has the title "albumNmame"
    PHAssetCollection *collection = [self fetchAssetCollectionWithAlbumName: albumName];

    if (collection == nil) 
        //If we were unable to find a collection named "albumName" we'll create it before inserting the image
        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^
            [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle: albumName];
         completionHandler:^(BOOL success, NSError * _Nullable error) 
            if (error != nil) 
                NSLog(@"Error inserting image into album: %@", error.localizedDescription);
            

            if (success) 
                //Fetch the newly created collection (which we *assume* exists here)
                PHAssetCollection *newCollection = [self fetchAssetCollectionWithAlbumName:albumName];
                [self insertImage:image intoAssetCollection: newCollection];
            
        ];
     else 
        //If we found the existing AssetCollection with the title "albumName", insert into it
        [self insertImage:image intoAssetCollection: collection];
    


- (PHAssetCollection *)fetchAssetCollectionWithAlbumName:(NSString *)albumName 
    PHFetchOptions *fetchOptions = [PHFetchOptions new];
    //Provide the predicate to match the title of the album.
    fetchOptions.predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"title == '%@'", albumName]];

    //Fetch the album using the fetch option
    PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:fetchOptions];

    //Assuming the album exists and no album shares it's name, it should be the only result fetched
    return fetchResult.firstObject;


- (void)insertImage:(UIImage *)image intoAssetCollection:(PHAssetCollection *)collection 
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^

        //This will request a PHAsset be created for the UIImage
        PHAssetCreationRequest *creationRequest = [PHAssetCreationRequest creationRequestForAssetFromImage:image];

        //Create a change request to insert the new PHAsset in the collection
        PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:collection];

        //Add the PHAsset placeholder into the creation request.
        //The placeholder is used because the actual PHAsset hasn't been created yet
        if (request != nil && creationRequest.placeholderForCreatedAsset != nil) 
            [request addAssets: @[creationRequest.placeholderForCreatedAsset]];
        
     completionHandler:^(BOOL success, NSError * _Nullable error) 
        if (error != nil) 
            NSLog(@"Error inserting image into asset collection: %@", error.localizedDescription);
        
    ];

【讨论】:

是否可以只添加到该相册而不添加到相机胶卷?【参考方案4】:

改编埃迪对 Swift 4 的回答:

    func saveImageToAlbum(_ image: UIImage, name: String) 

        if let collection = fetchAssetCollection(name) 
            self.saveImageToAssetCollection(image, collection: collection)
         else 
            // Album does not exist, create it and attempt to save the image
            PHPhotoLibrary.shared().performChanges(
                PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: name)
            , completionHandler:  (success: Bool, error: Error?) in
                guard success == true && error == nil else 
                    NSLog("Could not create the album")
                    if let err = error 
                        NSLog("Error: \(err)")
                    
                    return
                

                if let newCollection = self.fetchAssetCollection(name) 
                    self.saveImageToAssetCollection(image, collection: newCollection)
                
            )
        
    

    func fetchAssetCollection(_ name: String) -> PHAssetCollection? 

        let fetchOption = PHFetchOptions()
        fetchOption.predicate = NSPredicate(format: "title == '" + name + "'")

        let fetchResult = PHAssetCollection.fetchAssetCollections(
            with: PHAssetCollectionType.album,
            subtype: PHAssetCollectionSubtype.albumRegular,
            options: fetchOption)

        return fetchResult.firstObject
    

    func saveImageToAssetCollection(_ image: UIImage, collection: PHAssetCollection) 

        PHPhotoLibrary.shared().performChanges(

            let creationRequest = PHAssetCreationRequest.creationRequestForAsset(from: image)
            if let request = PHAssetCollectionChangeRequest(for: collection),
                let placeHolder = creationRequest.placeholderForCreatedAsset 
                request.addAssets([placeHolder] as NSFastEnumeration)
            
        , completionHandler:  (success: Bool, error: Error?) in
            guard success == true && error == nil else 
                NSLog("Could not save the image")
                if let err = error 
                    NSLog("Error: " + err.localizedDescription)
                
                return
            
        )
    

【讨论】:

【参考方案5】:

Objective C 的改进版本,使用块。如果相册不存在,它会创建一个相册,然后保存三种类型的媒体项目 - 照片、GIF 和视频:

// Types of media, that can be saved to an album
typedef NS_ENUM(NSUInteger, AlbumMediaType) 
    AlbumMediaTypePhoto,
    AlbumMediaTypeGIF,
    AlbumMediaTypeVideo
;

/**
 Creates album if it doesn't exist and returns it in a block
 */
- (void)createCollectionOnComplete:(void (^ _Nonnull)(PHAssetCollection * _Nonnull collection))onComplete

    NSString *albumTitle = @"YOUR_ALBUM_TITLE";

    __block PHAssetCollection *collection;
    __block PHObjectPlaceholder *placeholder;

    // Searching for an existing album
    PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
    fetchOptions.predicate = [NSPredicate predicateWithFormat:@"title = %@", albumTitle];
    collection = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
                                                          subtype:PHAssetCollectionSubtypeAny
                                                          options:fetchOptions].firstObject;
    // If album is not found, we create it
    if (!collection)
    
        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^
            PHAssetCollectionChangeRequest *createAlbum = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:albumTitle];
            placeholder = [createAlbum placeholderForCreatedAssetCollection];
         completionHandler:^(BOOL success, NSError *error) 
            if (success)
            
                PHFetchResult *collectionFetchResult = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[placeholder.localIdentifier]
                                                                                                            options:nil];
                collection = collectionFetchResult.firstObject;
                // After creating album, we return it
                onComplete(collection);
            
        ];
     else 
        // If album already exists, we instantly return it
        onComplete(collection);
    



/**
 Saves an item of a given mediatype, that is located in mediaURL
 */
- (void)saveToAlbumMediaItemFromURL:(NSURL *)mediaURL mediaType:(AlbumMediaType)mediaType

    NSData *mediaData = [NSData dataWithContentsOfURL:mediaURL];
    if (!mediaData) 
        OWSFail(@"%@ Could not load data: %@", self.logTag, [self.attachmentStream mediaURL]);
        return;
    

    [self createCollectionOnComplete:^(PHAssetCollection * _Nonnull collection) 

        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^

            // We create a PHAsset using creationRequest
            PHAssetCreationRequest *assetRequest;
            switch (mediaType) 
                case AlbumMediaTypePhoto: 
                    assetRequest = [PHAssetCreationRequest creationRequestForAssetFromImage:[UIImage imageWithData:mediaData]];
                    break;
                
                case AlbumMediaTypeGIF: 
                    assetRequest = [PHAssetCreationRequest creationRequestForAsset];
                    PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init];
                    [assetRequest addResourceWithType:PHAssetResourceTypePhoto data:mediaData options:options];
                    break;
                
                case AlbumMediaTypeVideo: 
                    if ( !UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(mediaURL.path) ) 
                        OWSFail(@"%@ Could not save incompatible video data.", self.logTag);
                        break;
                    

                    NSString *videoPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"file.mov"];
                    [mediaData writeToFile:videoPath atomically:YES];
                    assetRequest = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:videoPath]];
                    break;
                
                default:
                    break;
            

            // Creating a request to change an album
            PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:collection];

            // PHAsset is not created yet, so we use a placeholder
            PHObjectPlaceholder *placeholder = [assetRequest placeholderForCreatedAsset];

            // We add a placeholder of a created item to the request of changing album
            if (albumChangeRequest != nil && placeholder != nil) 
                [albumChangeRequest addAssets: @[placeholder]];
            

         completionHandler:^(BOOL success, NSError *error) 

            if (success) 
                NSLog(@"Media item saved!");
             else 
                NSLog(@"Error saving media item - %@", error ? error.localizedDescription : @"");
            

        ];

    ];

我们可以使用这些方法以这种方式保存媒体项目:

[self saveToAlbumMediaItemFromURL:[self.attachmentStream mediaURL] mediaType:AlbumMediaTypeGIF];

【讨论】:

以上是关于iOS 将照片保存在特定应用的相册中的主要内容,如果未能解决你的问题,请参考以下文章

将照片保存到iOS相册,不要全部保存

如何在 iOS 中使用 UIActivityViewController 创建和保存特定相册

如何在 iOS9 中使用 PHPhotolibrary 将照片保存在带有元数据的相册中?

PhotosFramework 将资产分配给相册

如何在 iOS/macOS 云共享相册中为照片附加短数据?

iOS开发之保存照片到系统相册(Photo Album)