Cloning UIImagePickerController using the Assets Library Framework

October 7th, 2010 Posted by: Collin - posted under:Featured » Tutorials

Hello iCoders. This is a follow up post to my initial post on the Assets Library Framework and Blocks. We came across an interesting problem when working on the application for . They have had an since the very early days of the app store, and one of our biggest struggles has been creating an interface to allow users to select multiple photos from their photo album for a slideshow. While the iPhone photos application allows for this, the UIImagePicker does not. With the release of the Assets Library framework we can now recreate the experience of the UIImagePicker, with some additional custom functionality to fit our needs. As a result we created a new cloned version of the UIImagePicker that allows for multiple photo selection just like the photos app. We have decided to open source the controller for other developers to use. This post will explain the process of creating the new image picker as well as the method of incorporating it into your code.

The ELCImagePickerController

The ELCImagePickerController is only possible because of the newly introduced Assets Library framework. This framework gives you raw (more or less) access to the photo and video elements of a user. We looked at UIImagePickerController and saw a lot of weaknesses with it. You can only select 1 photo at a time, and even in Apple’s photo app, where you can choose several pictures at a time, you can’t use multi touch to do your selection. To solve these problems we rolled our own solution that works very closely to UIImagePickerController.

How to use it

First I am going to explain using the picker since to many people the process of creating it won’t be very interesting. The image picker is created and displayed in a very similar manner to the UIImagePickerController. The sample application that is part of the GitHub project, where I distribute the controller, shows its use, but I will go into detail here. To display the controller you instantiate it and display it modally like so.

ELCImagePickerController *controller = [[ELCImagePickerController alloc] initImagePicker];
[controller setDelegate:self];
[self presentModalViewController:controller animated:YES];
[controller release];

The ELCImagePickerController will return the select images back to the ELCImagePickerControllerDelegate. The delegate contains to methods very similar to the UIImagePickerControllerDelegate. Instead of returning one dictionary representing a single image the controller sends back an array of similarly structured dictionaries. The two delegate methods are:]

- (void)elcImagePickerController:(ELCImagePickerController *)picker didFinishPickingMediaWithInfo:(NSArray *)info;
- (void)elcImagePickerControllerDidCancel:(ELCImagePickerController *)picker;

GIT Hub

You can find this project . Please let me know any issues you may have and look for future releases with feature enhancements

General Structure

The ELCImagePicker is a collection of UITableViewControllers that are placed inside a UINavigationController. While the ELCImagePickerController is actually 5 separate custom classes I have written, I have put them all within a single header and main. I chose this to make the classes that were required to be imported into a project when using this as few as possible.  While usually when presenting a UINavigationController you would create one yourself in code and use the initWithRootViewController method, in this case we have created a UINavigationController subclass called ELCImagePickerController which does all this behind the scenes. In the end all a developer has to do is use the initImagePicker method and present the controller modally. This lets the class match the functionality of UIImagePickerController closer. You can see the Header and Main for the ELCImagePickerController class on the next page.

from on .

  • http://www.architek.co.uk Free1000

    Thanks for making this open, I think its highly useful keeping this cloned to the appearance of the existing ImagePickerController.

    So far I’ve successfully run this in the simulator on the 4.2 beta SDK. The initial app doesn’t seem to run on my iPad with the 4.2 beta on it currently though, The initial ELCImagePickerDemoViewController doesn’t display the button. No idea why at the moment but if I have time next week to work with this a bit more I’ll let you know as I will try to integrate this into my own app and see how it goes later in the week.

    One question I have is how does the memory get reclaimed in the case of the following method
    -(void)selectedAssets:(NSArray*)_assets {

    NSMutableArray *returnArray = [[NSMutableArray alloc] init];

    for(ALAsset *asset in _assets) {

    NSMutableDictionary *workingDictionary = [[NSMutableDictionary alloc] init];
    [workingDictionary setObject:[asset valueForProperty:ALAssetPropertyType] forKey:@”UIImagePickerControllerMediaType”];
    [workingDictionary setObject:[UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage]] forKey:@”UIImagePickerControllerOriginalImage”];
    [workingDictionary setObject:[[asset valueForProperty:ALAssetPropertyURLs] valueForKey:[[[asset valueForProperty:ALAssetPropertyURLs] allKeys] objectAtIndex:0]] forKey:@”UIImagePickerControllerReferenceURL”];

    [returnArray addObject:workingDictionary];

    [workingDictionary release];
    }

    if([delegate respondsToSelector:@selector(elcImagePickerController:didFinishPickingMediaWithInfo:)]) {
    [delegate performSelector:@selector(elcImagePickerController:didFinishPickingMediaWithInfo:) withObject:self withObject:[NSArray arrayWithArray:returnArray]];
    }

    I’m relatively new to Obj-C and Cocoa touch from Java development so some memory handling remains for me to fully understand. Given that you allocate the member returnArray and then pass it via the delegate method does this mean the delegate needs to know that it has to release the returnArray itself? Or is there some other way the memory is released. I’d assume ( possibly incorrectly) that the ELCImagePickerController class would retain the NSMutableArray.

    Also, I haven’t yet implemented a delegate and I’m fascinated by this particular pattern.

    if([delegate respondsToSelector:@selector(elcImagePickerController:didFinishPickingMediaWithInfo:)]) {
    [delegate performSelector:@selector(elcImagePickerController:didFinishPickingMediaWithInfo:) withObject:self withObject:[NSArray arrayWithArray:returnArray]];

    As I have no idea to start with the implementation of delegates in Obj-c this is very instructional.

  • http://www.architek.co.uk Free1000

    PS: Although your source is open it is copyrighted and I can’t see any information about licensing. Do you have any information about this?

  • http://www.architek.co.uk Free1000

    Oops, just seen the MIT license! Ignore previous.

  • sam

    Hello,

    I found a major bug in ur app while syncing the photos.I have also made the slideshow using AssetsLibrary and facing the same problem.

    Just sync some new photos using itunes and try to acess those photos,only thumbail wd be shown and not the fullscreen/fullres images.

    Pls let me know if you have any solution to this.

    thanks

  • ct

    Any ideas how to implement selection of all the images from the AssetTablePicker..?

  • http://www.cokersolutions.com/forum mracoker

    @Sam
    I dont know about your thumbnail issue but I did notice that when using the ELCImagePickerController that I would not get the full resolution image. I have found how to change this and will share for you or others:

    Line #43 in ELCImagePickerController.m

    From:
    [workingDictionary setObject:[UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage]] forKey:@”UIImagePickerControllerOriginalImage”];

    To:
    [workingDictionary setObject:[UIImage imageWithCGImage:[[asset defaultRepresentation] fullResolutionImage]] forKey:@”UIImagePickerControllerOriginalImage”];

  • http://www.cokersolutions.com/forum mracoker

    This is great and thank you for making it available. I have question, I would like to show a UIView after the users selects their images. If a user selects 20 or more it appears that the app locks up, it doesnt it just looks that way. So I want to show a sort of “Please Wait” so the user sees that the app is busy. How is the best way to implement this?

    Thanks,

    Albert C.

  • John

    This is great. Thank you for the wonderful post!

    Question: How would we make this so the user is limited to picking only one image?

    Thanks again!

  • http://worldlbizexpress.com Bharath Rai

    First I would like to thank for this great tutorial.
    I come across one problem with this, when we have more number of images in image library, the application will crash. I think, because of putting images in array(memory issue). Is it possible to save images in database(sqlite, local memory)and then show in scroll view.

  • http://www.cokersolutions.com/forum mracoker

    I figured it out on my own.

    I basically had to show a sub view that is semi-transparent, remove the back button and done button.

    Next I had to show my sub view when the dismiss button was clicked in -(void)selectedAssets:(NSArray*)_assets in the AssetTablePicker implementation.

    Then I had to move -(void)selectedAssets:(NSArray*)_assets in ELCImagePickerController implementation to a background thread so the ui doesn’t lock up.

    Thanks,

    Albert C.

  • paolo

    Please, help.
    How to use it?

    ELCImagePickerController *controller = [[ELCImagePickerController alloc] initImagePicker];
    [controller setDelegate:self];
    [self presentModalViewController:controller animated:YES];
    //This is an error:
    [controller selectedAssets:MY_IMAGE_ARRAY];??
    //Thank you

    [controller release];

  • http://www.cokersolutions.com/forum Albert Coker

    It looks like you are trying to pass a array of images to the picker. That is not how it works. ELCImagePickerController allows you to pick images, then passes them back via the delegate to where ever you initiated it from.

  • CoderGirl

    Hi, firstly thank you very much for the code. The code runs perfectly well in Simulator 4.1 but when I try 4.0 or 4.2 it gets stuck on the Loading screen and it gives me the following comment on the GDB
    2010-11-16 16:37:27.514 ELCImagePickerDemo[49819:6b03] A problem occured

    Its from this line in the code.

    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library enumerateGroupsWithTypes:ALAssetsGroupAll
    usingBlock:assetGroupEnumerator
    failureBlock:^(NSError *error) {
    NSLog(@"A problem occured");
    }];
    Somehow I am not able to figure out what is the problem, I have added the Asset Lib framework and everything. Please help.

  • samar

    nice article!!!

  • Andrew

    Hi,

    Great article. Just having some problems with the source code provided. Using simulator 4.2. and the latest xcode (downloaded last night), It builds fine, loads the album viewer, but when it loads up the images in the album, and then I select them, all the images just disappear. When I try clicking anywhere else it just crashes.

    Console doesn’t spit out anything either which is annoying because I cant see whats going wrong.

    Help?

  • DaveR

    CoderGirl, were you ever able to run this on a 4.2 device?

  • CoderGirl

    Hi, yes its working on 4.2, I had to makes some changes though to suit my needs, following is the change I made :-

    -(void)preparePhotos {

    dispatch_async(dispatch_get_main_queue(), ^{

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSLog(@”hmmm 1″);
    void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
    NSLog(@”hmmm 2″);
    if(group != nil) {
    [assetGroups addObject:group];
    NSLog(@”Number of assets in group %d”, [group numberOfAssets]);
    }
    else {
    NSLog(@”group is nil”);
    }

    [self performSelectorOnMainThread:@selector(reloadTableView) withObject:nil waitUntilDone:NO];
    };

    assetGroups = [[NSMutableArray alloc] init];

    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library enumerateGroupsWithTypes:ALAssetsGroupAll
    usingBlock:assetGroupEnumerator
    failureBlock:^(NSError *error) {
    NSLog(@"A problem occurred %@",error);
    }];
    [library release];
    [pool release];
    NSLog(@”hmmm 3″);

    });
    }

    Hope it works for you.

  • Paul Freeman

    I’ve found a problem with the code that prepares the group table. A couple of subtle problems are happening that I don’t yet understand. However, if you remove the NSLog statement from the closure then two problems appear.

    ie : remove the line

    NSLog(@”Number of assets in group=%d”, [group numberOfAssets]);

    from the prepareImages method in the group level picker

    void (^assetGroupEnumerator)(struct ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
    if(group != nil) {
    [assetGroups addObject:group];
    //NSLog(@”Number of assets in group=%d”, [group numberOfAssets]);
    }
    [self performSelectorOnMainThread:@selector(reloadTableView) withObject:nil waitUntilDone:NO];
    };

    Now there are two things that occur

    1. The count numbers for number of assets may not appear in the subsequent code that formats the table rows. Sometimes the count is incorrectly stated as zero.

    2. If the user does not create an albums in the photo library but just syncs their images with the library as a whole, then when you pick the ‘PhotoLibrary’ group or the ‘Shared Photos’ group (these are the only ones which are visible in this case) then the user will see no photos found.

    I’ve replaced the NSLog line with the following

    int count = [group numberOfAssets]; //ignore the warning from not using ‘count’

    And the code still works with this, so it looks as though there is something subtle going on here with how the closure is executing. I’ve no idea if this is a bug with the AssetsLibrary, but its certainly worth looking at as it could cause a serious problem with an app.

  • Felix

    Trying to get this to work so far so good however my “DONE” button is not appearing when your in the picker it shows the cancel fading out but then fading back in it doesnt change to done so i cant really chose any images. Where do you change the nav bar from cancel to done?

  • http://hubpages.com/profile/larryfreeman Larry

    When I run it, it seems that the orientation of the image gets lost. When I check the imageOrientation that gets saved in the dictionary, it also returns:

    UIImageOrientationUp

    Here’s how I’m checking the orientation.


    UIImage *image = [dict objectForKey:UIImagePickerControllerOriginalImage];
    UIImageOrientation orient = image.imageOrientation;

    If you have any suggestions for figuring out the correct orientation of the image added to the dictionary, that would be great.

    Thanks,

    -Lary

  • http://hubpages.com/profile/larryfreeman Larry

    OK, I figured it out how to add orientation to the code. Here’s the code:

    ALAssetRepresentation *representation = [asset defaultRepresentation];
    CGImageRef imageRef = [representation fullScreenImage];
    ALAssetOrientation orientation = [representation orientation];
    UIImage *image = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:(UIImageOrientation)orientation];

    [workingDictionary setObject:image forKey:@"UIImagePickerControllerOriginalImage"];

  • DesiGuy421

    Hi,

    Thanks for making this. This is saving me time instead of having to roll my own.

    With that said, there are several places in this code with glaring memory leaks. I have already forked and committed a fix for one that I have submitted a pull request for. There are others that I’ve discovered that could’ve been avoided with an autorelease call but are much more deeply rooted, and I am, as of right now, unsure of what consequences might result in fixing them.

    Regards.

  • http://oozware.com Jung

    Hi. thanks for sharing such a good code

    when i use your code, I suddenly have a little thing to ask

    have you tried to load photo library that over 500 photos inside?

    when i load more than 500 photos it takes so long to load whole images

    can i have your advice to get rid of this one?

  • Stefan

    I tried your sample code on several iPhones and it’s working only if there are not so many pictures stored on the device, once a folder has more then 200 images there is a delay and its shows loading for a few seconds.

    However, I have one iPhone 4 with more then 800 images in several folders and it doesn’t even go past the “loading” stage.

    I have one app called ” Stash Pro” (hedonic software) and they are able to open the photo library without any delay (even with 800 images) and you can import several pictures or even the complete folder.

    Did they use a modified version of this script ? Or does anyone know how they made it ?

    Thanks for your advise.

  • Jed Lau

    I’ve experienced this problem as well in my own code. Have you uncovered any reasons why this might be occurring? Thanks.

  • CodeBear

    It is not working on my iPhone 3GS with iOS4.2.1. It worked on the simulator but it seems that it’s not able to find the photo directory in the device. The behaviour is similar to no photo in the simulator. It said “Loading…”

  • http://twitter.com/haentz Hans Schneider

    Thanks a lot for making this library available! Unfortunately I have the same issue as CodeBear. On both my 4.2.1 developer phones and also on my 4.3beta iPad the assetGroupEnumerator does not work. The last output in the console is:
    sandboxd[944] : ELCImagePickerDe(942) deny file-write-data /private/var/mobile/Media/PhotoData

    It looks like the system is keeping the app from accessing the album data (which indeed is outside the app sandbox). I read through Apple’s documentation on Assets and tried various stuff but I always end up with this error. Does anyone have an idea what the issue might be? Has anyone probably tested the code on a non-developer iPhone running 4.2.1 (with an ad hoc certificate for example or with an app from the app store)?

    At the moment I’m really a bit lost with this problem. Of course I understand it’s not an issue with ELCImagePickerController, but rather with the iPhone 4.2.1 API and/or system.

  • http://twitter.com/haentz Hans Schneider

    Ok, looks like I found the problem. It seems that you can only make calls to the ALAssetsLibrary in the app’s main thread in 4.2 (or probably even earlier.

    I changed the following line in the implementation of ELCImagePickerController.

    [self performSelectorInBackground:@selector(preparePhotos) withObject:nil];
    becomes
    [self preparePhotos];

  • SivaKumar

    Hi ,

    i buildup this code to iPad.i am using IOS SDK 4.2.When i’m pressing the “Launch ELC Image Picker Controller Button” it shows only empty table still the navigation bar holds the message “Loading..”.But in simulator it works fine.Please help to short it out.

  • addon.hrushikesh

    i am new in iphone. i want to develop the webservice application. i am developing the application for the welcome view and it is reading the message from the webservice xml file .i am puting the code in the -viewdidload function but it is displaying the message.

  • Jabroni

    I’m just checking to see if you were able to resolve this? I have the same issue.

  • http://twitter.com/Designergianna gianna

    I am a newbie to i-phone, i came across this word cloning in Java long back, user interface is more important because most of the people rely on user friendly application,..It seems that you can only make calls to the ALAssetsLibrary in the app’s main thread in 4.2 (or probably even earlier.