24

I'm trying to create a custom album in the Photo Library of an iPhone and then save photos that I've taken with the camera, or chosen from the phones Camera Roll to that custom album. I can successfully create the album but the photos are not getting saved there, instead they are getting saved to the simulators Saved Photos album... I'm not sure how to tell UIImageWriteToSavedPhotosAlbum to save to the new album I've just created using addAssetsGroupAlbumWithName...

Here is the code I have so far - I've snipped out a few sections to keep my code example short...

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{     
  NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
  if([mediaType isEqualToString:(__bridge NSString *)kUTTypeImage])
  {        
    // pull GPS information from photos metadata using ALAssetsLibrary
    void (^ALAssetsLibraryAssetForURLResultBlock)(ALAsset *) = ^(ALAsset *asset)
    {
        // code snipped out 
    };
    NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL];
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library assetForURL:assetURL
             resultBlock:ALAssetsLibraryAssetForURLResultBlock
            failureBlock:^(NSError *error) 
            {
                // code snipped out
            }];

    // getimage from imagePicker and resize it to the max size of the iPhone screen 
    UIImage *originalImage = [info objectForKey:UIImagePickerControllerOriginalImage]; 
    UIImage *resizedImage = [util_ createThumbnailForImage:originalImage thumbnailSize:[util_ determineIPhoneScreenSize]];
    NSData *imageData = UIImagePNGRepresentation(resizedImage);

                // code snipped out
                // code snipped out
                // code snipped out
                // code snipped out
                // code snipped out
                // code snipped out



    // create a new album called "My Apps Photos"
    [library addAssetsGroupAlbumWithName:@"My Apps Photos"
            resultBlock:^(ALAssetsGroup *group) 
            {
                NSLog(@"in addAssetsGroupAlbumWithName resultBlock");

                // save file to album
                UIImageWriteToSavedPhotosAlbum(resizedImage, self, nil, nil);

            } 
            failureBlock:^(NSError *error) 
            {
                NSLog(@"in addAssetsGroupAlbumWithName failureBlock");

            }
     ];
  }
}

So... Like I said, it creates the new album but does not save the photo there. How do I tell it to save into the new album? Perhaps I sound not use UIImageWriteToSavedPhotosAlbum??

Note: I'm using Xcode 4.3.2, IOS 5.1, and ARC

ElasticThoughts
  • 3,208
  • 6
  • 41
  • 55

5 Answers5

60

If you are using iOS6, Fernando's answer will not work, because the saveImage selector is no longer available.

The process is pretty confusing, and I have not seen any clear answers posted, so here is the method I've used to solve this in iOS6.

You will need to use a combination of the following:

Create the Album:

[self.library addAssetsGroupAlbumWithName:albumName 
                              resultBlock:^(ALAssetsGroup *group) {
         NSLog(@"added album:%@", albumName);
}
                             failureBlock:^(NSError *error) {
         NSLog(@"error adding album");
}];

Find the Album:

__block ALAssetsGroup* groupToAddTo;
[self.library enumerateGroupsWithTypes:ALAssetsGroupAlbum
                             usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
      if ([[group valueForProperty:ALAssetsGroupPropertyName] isEqualToString:albumName]) {
          NSLog(@"found album %@", albumName);
          groupToAddTo = group;
      }
}
                           failureBlock:^(NSError* error) {
     NSLog(@"failed to enumerate albums:\nError: %@", [error localizedDescription]);
}];

Save the Image to Asset Library, and put it into the album:

CGImageRef img = [image CGImage];
[self.library writeImageToSavedPhotosAlbum:img
                                  metadata:[info objectForKey:UIImagePickerControllerMediaMetadata]
                           completionBlock:^(NSURL* assetURL, NSError* error) {
     if (error.code == 0) {
         NSLog(@"saved image completed:\nurl: %@", assetURL);

         // try to get the asset
         [self.library assetForURL:assetURL
                       resultBlock:^(ALAsset *asset) {
              // assign the photo to the album
              [groupToAddTo addAsset:asset];
              NSLog(@"Added %@ to %@", [[asset defaultRepresentation] filename], albumName);
          }
                      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]);
     }
 }];
Scott Allen
  • 1,199
  • 1
  • 12
  • 14
  • 1
    i get an issue with our code causing an EXC BAD ACCESS on the '[groupToAddTo addAsset:asset];' - Any ideas please? Cheers.. – theiOSDude Dec 20 '12 at 16:48
  • One possible cause is that your library variable is no longer valid - you need your library to stay around for as long as you are accessing objects that you generated using it; i.e. you can't have the library get dealloc'd and then try to run a selector on groupToAddTo...I typically make the library a property, and initialize it in viewDidLoad. – Scott Allen Dec 23 '12 at 03:19
  • Fernando's is depreceated, i solved the problem by adding the writeImageToSavedPhotosAlbum block to just after assigning 'groupToAddTo = group' and used the 'group' variable instead of 'groupToAddTo :) Cheers for your help – theiOSDude Jan 02 '13 at 10:47
  • @ScottAllen by inspiring with your code I added swift version below. – Warewolf Nov 10 '14 at 11:02
  • 1
    when try this code my image is save in gallery and also custom album but i want to only save in custom album if any way then guide me – sanjeet Jul 23 '15 at 10:15
  • Save the image first time only, don't know why! – Jamil Sep 21 '16 at 05:31
12

Fernando's answer worked for me in iOS 7.

Steps :

1) Download the ALAssetsLibrary+CustomPhotoAlbum code from here: http://www.touch-code-magazine.com/wp-content/uploads/2011/11/ALAssetsLibrary_CustomPhotoAlbum.zip?a071b6 and copy the 2 files for the category inside your Xcode project.

2)In your header file,add the following lines

#import <AssetsLibrary/AssetsLibrary.h>
#import "ALAssetsLibrary+CustomPhotoAlbum.h"

@property (strong, atomic) ALAssetsLibrary* library;

3) In your implementation file,add the following lines

@synthesize library=_library;

EDIT: 4) initialise the asset library instance, preferably in "viewDidLoad" method, otherwise the saveImage method below won't execute. (CONFIRMED):

[I am resubmitting the suggested edit because someone with non-iphone skills rejected the previous submission. This is undoubtedly a great answer by @santhu & Fernando and helped me a lot, however, the initialisation piece of code was missing so it took me a bit of time to figure out why the code didn't work. Hence, I would appreciate if a moderator with iPhone development skillset reviews the edit.]

_library = [[ALAssetsLibrary alloc] init];

5) Add this in the method where you wish to save

  //Add this in the method where you wish to save
  [self.library saveImage:(UIImage *) toAlbum:(NSString *) withCompletionBlock:^(NSError                 *error) {
    if (error!=nil) {
        NSLog(@"Big error: %@", [error description]);
    }
}];
programmer
  • 138
  • 9
Santhu
  • 151
  • 1
  • 4
9

For swift users:- I made function to do the same thing.

declare closure outsize class definition (above class definition)

typealias CompletionHandler = (success:Bool!) -> Void  

declare library variable inside class

var library:ALAssetsLibrary?;

initialize variable in viewDidLoad

library = ALAssetsLibrary();

method to add Image to Particular Album

func addImage(image:UIImage, metaData:NSDictionary, toAlbum albumName:String, handler:CompletionHandler){

    library?.addAssetsGroupAlbumWithName(albumName, resultBlock: {(group:ALAssetsGroup!) -> Void in
        print("\nAlbum Created:=  \(albumName)");
        /*-- Find Group --*/

        var groupToAddTo:ALAssetsGroup?;

        self.library?.enumerateGroupsWithTypes(ALAssetsGroupType(ALAssetsGroupAlbum),
            usingBlock: { (group:ALAssetsGroup?, stop:UnsafeMutablePointer<ObjCBool>) -> Void in

                if(group != nil){

                    if group!.valueForProperty(ALAssetsGroupPropertyName) as String == albumName{
                        groupToAddTo = group;

                        print("\nGroup Found \(group!.valueForProperty(ALAssetsGroupPropertyName))\n");

                        self.library?.writeImageToSavedPhotosAlbum(image.CGImage, metadata:metaData, completionBlock: {(assetURL:NSURL!,error:NSError!) -> Void in

                            if(error == nil){
                                self.library?.assetForURL(assetURL,
                                    resultBlock: { (asset:ALAsset!) -> Void in
                                        var yes:Bool? = groupToAddTo?.addAsset(asset);
                                        if (yes == true){
                                            handler(success: true);
                                        }
                                    },
                                    failureBlock: { (error2:NSError!) -> Void in
                                        print("Failed to add asset");
                                        handler(success: false);
                                });
                            }
                        });
                    }
                } /*Group Is Not nil*/
            },
            failureBlock: { (error:NSError!) -> Void in
                print("Failed to find group");
                handler(success: false);
        });

        }, failureBlock: { (error:NSError!) -> Void in
            print("Failed to create \(error)");
            handler(success: false);
    });
}

call this method as :--

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]){

    var image:UIImage = info[UIImagePickerControllerOriginalImage] as UIImage;
    var metadata:NSDictionary = info[UIImagePickerControllerMediaMetadata] as NSDictionary;

    self.addImage(image, metaData: metadata, toAlbum: "SwiftAlbum") { (success) -> Void in
        print("Image Added : \(success)");
    }

    picker.dismissViewControllerAnimated(true, completion: nil);
}
Warewolf
  • 12,416
  • 4
  • 55
  • 85
  • Do be careful with the metaData bit in self.addImage(image, metaData: metadata, toAlbum: "SwiftAlbum") . Because if there isn't any (which is quite often the case) you'll get an error – TimWhiting Mar 28 '15 at 00:44
  • 3
    ALAssetsLibrary will be Deprecated in iOS 9.0 – Lucas Sep 01 '15 at 19:34
3

The code from @Scott Allen was close but would not save the image for me the first time. So if I did not already have the album created, the image would not save. My solution was to move this snippet that creates the album to the app delegate didFinishLaunchingWithOptions:

    NSString *albumName=@"album name";
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library addAssetsGroupAlbumWithName:albumName
                         resultBlock:^(ALAssetsGroup *group) {
                             NSLog(@"added album:%@", albumName);
                         }
                        failureBlock:^(NSError *error) {
                            NSLog(@"error adding album");
                        }];
2

I haven't seen really clear answers and code to these questions. For me, I wanted to make sure that the album was found or created, before I fire up the camera. This code seems to be about right, and I think it is a bit cleaner and easier to steal^H^H^H^H^Hstart from.

// find or create our photo album.  If either works
// we fire up the camera. Crazy asynchronous code here.

__weak PhotoVC *weakSelf = self;
__block BOOL found = NO;

ALAssetsLibraryGroupsEnumerationResultsBlock
assetGroupEnumerator = ^(ALAssetsGroup *group, BOOL *stop){
    if (group) {
        NSString *thisGroup = [group valueForProperty:ALAssetsGroupPropertyName];
        if ([album isEqualToString:thisGroup]) {
            NSLog(@"album found!");
            [weakSelf startCamera: group];
            *stop = YES;
            found = YES;
        }
    } else { // not found, create the album
        if (found)
            return;
        NSLog(@"album not found, try making album");

        ALAssetsLibraryGroupResultBlock addGroup =
        ^(ALAssetsGroup *group){
            NSLog(@"album created");
            [weakSelf startCamera: group];
        };

        ALAssetsLibraryAccessFailureBlock addGroupFailed =
        ^(NSError *err){
            NSLog(@"add group failed: %@", [err localizedDescription]);
        };

        [library addAssetsGroupAlbumWithName:album resultBlock:addGroup failureBlock:addGroupFailed];
    }
};

[library enumerateGroupsWithTypes:ALAssetsGroupAlbum
                       usingBlock:assetGroupEnumerator
                     failureBlock:^(NSError *error) {
                         NSLog(@"album access denied");
                     }];

Tom Duff's law: steal code whenever you can.

Bill Cheswick
  • 615
  • 7
  • 12