sachi
sachi

Reputation: 2132

iPhone - How to create a custom album and give custom names to photos in camera roll programmatically?

I am developing an iPhone photo application, so i need to create a separate album with a name "My Album" in camera roll and i need to save my UIImageView image with custom name for example "My Image.png" inside the newly created directory.

How can i do this?

Upvotes: 23

Views: 26239

Answers (7)

ricardopereira
ricardopereira

Reputation: 11683

Since the AssetsLibrary is deprecated, please use the Photos framework instead (iOS 8 and later).

// Deprecated!
import AssetsLibrary

// Swift 3.0
let assetsLibrary = ALAssetsLibrary()
assetsLibrary.addAssetsGroupAlbum(withName: "NewAlbum", resultBlock: { assetsGroup in
    print(assetsGroup == nil ? "Already created" : "Success")
}, failureBlock: { error in
    print(error)
})

You can use the shared PHPhotoLibrary object to create new photos but you can't give them specific names because you will be working with assets that need to be managed by the Photos.app. Each asset has specific properties. You can fetch objects, request changes, asset/thumbnail loading and caching, etc.

To create a custom album, please use the PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle:).

Brief example:

// Swift 3.0
func createPhotoLibraryAlbum(name: String) {
    var albumPlaceholder: PHObjectPlaceholder?
    PHPhotoLibrary.shared().performChanges({
        // Request creating an album with parameter name
        let createAlbumRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: name)
        // Get a placeholder for the new album
        albumPlaceholder = createAlbumRequest.placeholderForCreatedAssetCollection
    }, completionHandler: { success, error in
        if success {
            guard let placeholder = albumPlaceholder else {
                fatalError("Album placeholder is nil")
            }

            let fetchResult = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [placeholder.localIdentifier], options: nil)
            guard let album: PHAssetCollection = fetchResult.firstObject else {
                // FetchResult has no PHAssetCollection
                return
            }

            // Saved successfully!
            print(album.assetCollectionType)
        }
        else if let e = error {
            // Save album failed with error
        }
        else {
            // Save album failed with no error
        }
    })
}

Don't forget to import Photos library.

To create a new photo asset on that album, please use the PHAssetChangeRequest.creationRequestForAsset(from:).

// Swift 3.0
func createPhotoOnAlbum(photo: UIImage, album: PHAssetCollection) {
    PHPhotoLibrary.shared().performChanges({
        // Request creating an asset from the image
        let createAssetRequest = PHAssetChangeRequest.creationRequestForAsset(from: photo)
        // Request editing the album
        guard let albumChangeRequest = PHAssetCollectionChangeRequest(for: album) else {
            // Album change request has failed
            return
        }
        // Get a placeholder for the new asset and add it to the album editing request
        guard let photoPlaceholder = createAssetRequest.placeholderForCreatedAsset else {
            // Photo Placeholder is nil
            return
        }
        albumChangeRequest.addAssets([photoPlaceholder] as NSArray)
    }, completionHandler: { success, error in
        if success {
            // Saved successfully!
        }
        else if let e = error {
            // Save photo failed with error
        }
        else {
            // Save photo failed with no error
        }
    })
}

UPDATE:

We need to request access to be able to use the Photos library:

PHPhotoLibrary.requestAuthorization { status in
     switch status {
     ...
}

As of iOS 10 and above we also need to add entry for access in the target .plist file for "Privacy - Photo Library Usage Description":

<key>NSPhotoLibraryUsageDescription</key>
<string>Access to photos is needed to provide app features</string>

Upvotes: 20

shunag chen
shunag chen

Reputation: 1

you can save image with custom name to sandbox first,and then save it to album,it work to me.

Upvotes: 0

nslllava
nslllava

Reputation: 599

Create a new album:

/// Create album with given title
/// - Parameters:
///   - title: the title
///   - completionHandler: the completion handler
func createAlbum(withTitle title: String, completionHandler: @escaping (PHAssetCollection?) -> ()) {
    DispatchQueue.global(qos: .background).async {
        var placeholder: PHObjectPlaceholder?
        
        PHPhotoLibrary.shared().performChanges({
            let createAlbumRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: title)
            placeholder = createAlbumRequest.placeholderForCreatedAssetCollection
        }, completionHandler: { (created, error) in
            var album: PHAssetCollection?
            if created {
                let collectionFetchResult = placeholder.map { PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [$0.localIdentifier], options: nil) }
                album = collectionFetchResult?.firstObject
            }
            
            completionHandler(album)
        })
    }
}

Get an album with with a specified name:

/// Get album with given title
/// - Parameters:
///   - title: the title
///   - completionHandler: the completion handler
func getAlbum(title: String, completionHandler: @escaping (PHAssetCollection?) -> ()) {
    DispatchQueue.global(qos: .background).async { [weak self] in
        let fetchOptions = PHFetchOptions()
        fetchOptions.predicate = NSPredicate(format: "title = %@", title)
        let collections = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
        
        if let album = collections.firstObject {
            completionHandler(album)
        } else {
            self?.createAlbum(withTitle: title, completionHandler: { (album) in
                completionHandler(album)
            })
        }
    }
}

And save a photo to a Photos album:

func save(photo: UIImage, toAlbum titled: String, completionHandler: @escaping (Bool, Error?) -> ()) {
    getAlbum(title: titled) { (album) in
        DispatchQueue.global(qos: .background).async {
            PHPhotoLibrary.shared().performChanges({
                let assetRequest = PHAssetChangeRequest.creationRequestForAsset(from: photo)
                let assets = assetRequest.placeholderForCreatedAsset
                    .map { [$0] as NSArray } ?? NSArray()
                let albumChangeRequest = album.flatMap { PHAssetCollectionChangeRequest(for: $0) }
                albumChangeRequest?.addAssets(assets)
            }, completionHandler: { (success, error) in
                completionHandler(success, error)
            })
        }
    }
}

Upvotes: 8

DawnSong
DawnSong

Reputation: 5202

/// Save images or videos(保存图片或视频)(kUTTypeImage, kUTTypeMovie)
/// Add to album if specified album name, and create album if needed
/// @params mediaArray UIImage, fileURL for a image or video
+ (void)_saveMediaArray:(NSArray *)mediaArray
                options:(LAImageSaverOptions *)options
             completion:(void (^)(NSError * _Nullable err))completion
{
    NSInteger __block count = 0;

    [PHPhotoLibrary.sharedPhotoLibrary performChanges:^{

        // Create album if needed
        PHAssetCollectionChangeRequest *assetCollectionChangeRequest = nil;
        NSMutableArray<PHObjectPlaceholder *> *assetChangeRequestPlaceholders = nil;
        if (options.targetAlbumName.length > 0) {
            assetChangeRequestPlaceholders = [NSMutableArray arrayWithCapacity:mediaArray.count];

            PHFetchOptions *fetchOptions = PHFetchOptions.new;
            //fetchOptions.includeAssetSourceTypes = PHAssetSourceTypeUserLibrary;
            fetchOptions.predicate = [NSPredicate predicateWithFormat:@"localizedTitle = %@", options.targetAlbumName]; // 不能用 block 形式的 predicate
            PHAssetCollection * assetCollection = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:fetchOptions].firstObject;
            if (nil == assetCollection) {
                assetCollectionChangeRequest = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:options.targetAlbumName];
            } else {
                assetCollectionChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];
            }
         }

        // Save images
        for (id item in mediaArray) {
            PHAssetChangeRequest *assetChangeRequest = nil;
            // image object
            if ([item isKindOfClass:UIImage.class]) {
                UIImage *image = (UIImage *)item;
                assetChangeRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
                [assetChangeRequestPlaceholders addObject:assetChangeRequest.placeholderForCreatedAsset];
                ++count;
                continue;
            }

            // file url for image or movie
            NSURL *fileURL = (NSURL *)item;
            if ([item isKindOfClass:NSURL.class] && fileURL.isFileURL) {
                NSString *extension = fileURL.pathExtension;
                if (extension.length == 0) {
                    NSLog(@"illegal fileURL(no path extension): %@", fileURL);
                    continue; // illegal file url
                }
                CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
                BOOL isImage = false;
                BOOL isVideo = false;
                if (nil != uti && CFStringGetLength(uti) > 0) {
                    isImage = UTTypeConformsTo(uti, kUTTypeImage);
                    isVideo = UTTypeConformsTo(uti, kUTTypeMovie); // kUTTypeVideo, kUTTypeAudiovisualContent
                }
                if (isImage) {
                    assetChangeRequest = [PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:fileURL];
                    [assetChangeRequestPlaceholders addObject:assetChangeRequest.placeholderForCreatedAsset];
                    ++count;
                } if (isVideo) {
                    assetChangeRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:fileURL];
                    [assetChangeRequestPlaceholders addObject:assetChangeRequest.placeholderForCreatedAsset];
                    ++count;
                } else {
                    NSLog(@"illegal fileURL(neither image nor movie): %@", fileURL);
                    continue; // illegal file url
                }
            }
        }

        // add to album if needed
        [assetCollectionChangeRequest addAssets:assetChangeRequestPlaceholders];

    } completionHandler:^(BOOL success, NSError * _Nullable error) {

        // not in main thread 
        dispatch_async(dispatch_get_main_queue(), ^{
            completion(error);
        });
    }];
}

By the way, you can do more about LAImageSaverOptions

@interface LAImageSaverOptions : NSObject

/// to show alert controller on the hostVC
@property(nonatomic, weak, null_resettable) UIViewController *hostVC;


/// total progress
@property (nonatomic, strong, null_resettable) NSProgress *progress;

// album name for saving images
@property (nonatomic, copy, nullable) NSString *targetAlbumName;

@end

Upvotes: 0

NDY
NDY

Reputation: 3557

You can create a custom album and add an image pretty easy with these lines of code in iOS:

// Create the new album.
__block PHObjectPlaceholder *myAlbum;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    PHAssetCollectionChangeRequest *changeRequest = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
    myAlbum = changeRequest.placeholderForCreatedAssetCollection;
} completionHandler:^(BOOL success, NSError *error) {
    if (success) {
        PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[myAlbum.localIdentifier] options:nil];
        PHAssetCollection *assetCollection = fetchResult.firstObject;

        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
            PHAssetChangeRequest *assetChangeRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];

            // add asset
            PHAssetCollectionChangeRequest *assetCollectionChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];
            [assetCollectionChangeRequest addAssets:@[[assetChangeRequest placeholderForCreatedAsset]]];
        } completionHandler:^(BOOL success, NSError *error) {
            if (!success) {
                NSLog(@"Error: %@", error);
            }
        }];
    } else {
        NSLog(@"Error: %@", error);
    }
}];

Upvotes: 18

Gaurav
Gaurav

Reputation: 168

You can try My below Method for Create Album for iOS 7 and iOS 8

#define PHOTO_ALBUM_NAME @"AlbumName Videos"
-(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");
    }];
}}

Upvotes: 0

Vaibhav Sharma
Vaibhav Sharma

Reputation: 1123

It was working from since iOS 5.0.
Please import AssetsLibrary/AssetsLibrary.h

ALAssetsLibrary* libraryFolder = [[ALAssetsLibrary alloc] init];
[libraryFolder addAssetsGroupAlbumWithName:@"My Album" resultBlock:^(ALAssetsGroup *group) 
{
    NSLog(@"Adding Folder:'My Album', success: %s", group.editable ? "Success" : "Already created: Not Success");
} failureBlock:^(NSError *error) 
{
    NSLog(@"Error: Adding on Folder");
}];

Upvotes: 7

Related Questions