Cloning UIImagePickerController using the Assets Library Framework

  • Twitter
  • Facebook
  • Digg
  • Reddit
  • StumbleUpon
  • del.icio.us
  • Google Bookmarks
October 7th, 2010 Posted by: (ELC) - 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 Animoto.com. They have had an app in the store 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 now on GitHub. 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.

ELCImagePickerControllerDemo from Collin Ruffenach on Vimeo.

Pages: 1 2 3 4

  • 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.

  • brian

    Hi, I believe that line exists in both the AssetTablePicker and AlbumPickerController classes. Did you change both?

  • http://www.icodeblog.com/2011/03/03/update-elcimagepickercontroller/ Update: ELCImagePickerController | iPhone Programming Tutorials

    [...] Cloning UIImagePickerController using the Assets Library Framework [...]

  • brian

    Have you guys figured anything out on this front? I experienced the same problem. I ended up replacing the NSLog line with just “[group numberOfAssets];”. I just noticed an update to the ELCImagePicker code though, so perhaps there’s a fix in there?

  • Miles_c

    Very nice tutorial, especially the exposure to ALAssets. I’m trying to get some persistence on the overlayView that was checked by the user to show up the next time they enter the AssetTablePicker. I thought I might be able to do this by setting the ELCAsset self.selected in the toggleSelection method. When I look at self.elcAssets in doneAction it shows all the Assets that have been selected, but when I try to access self.elcAssets in preparePhotos that array is empty, is that because of threading?

    Also, would it be better to iterate through the Assets during doneAction and write them out to a .plist then read them back in just before Enumerating them in preparePhotos?

  • Stfoll

    Hello all, Thanks fort this app, it works wonderful, but how can I deselect a thumb programmatically ? I tried a lot of ways , but no success . Thanks

  • Stfoll

    Oops sorry, it is so easy :) ) setSelected:(BOOL)

  • http://twitter.com/jonathancarroll Jon Carroll

    I am having a pretty weird issue with the picker and was wondering if anyone might have some ideas. I am using the picker in an iPad application that allows you to import both images and video. I am using the ELCImagePicker for importing images and the standard UIImagePickerController for video.

    The problem is that after displaying the UIImagePicker controller and successfully bringing in a video the ELCImagePickerController will only display folder/album counts for the number of videos in each album and will then show no pictures or video in any of the albums. If I present the ELCImagePicker controller before importing a video with UIImagePickerController it works fine.

    I suspect this may be a bug as I have looked through the ELCImagePicker code and you seem to be doing everything right. Any thoughts are appreciated. Thanks!

  • aofeng2009

    Good tutorial , When an app accesses the assets library for the first time an alert view vill pop-up to ask the user to allow the access to the device’s assets. If i tap the “not allowed”, The system will always forbid me access the assets even if i restart the app,how can i access the assets second?

  • Timo

    you have to go to the iphone settings and then grant the app access there. It would be a nicer way to catch the error and show a pop up that explains the user why he has to activate this setting. And direct him to the settings. Any idea how to do this?

  • aofeng2009

    How can i grant the app access ,I can’t find that in iphone’s settings.Any idea?

  • http://twitter.com/jonathancarroll Jon Carroll

    You might have to re-install the app after denying it, not certain though.

    I present a UIAlertView to the user before the image picker comes up for the first time explaining why it is about to ask for their location and that the functionality won’t work without it. Seems to work well, haven’t had any users complain yet.

  • imobilizer

    Hello,

    My app allows the user to pick photos and videos for upload via UIImagePickerController. My issue is that regardless of the UIImagePickerController.videoQuality value I set, once I Tap “Choose” on the preview screen the video gets transcoded (I see the compressing video progress bar). This is fine since I need to be sure videos can be uploaded over a cellular connection. The issue is that if I put the app into the background (while compressing) the compression seems to be halted. When I return to the app I see the progress bar has not moved and does not resume. What would be required to emulate the preview screen/choose/transcode functionality and manage the transcoding in the background or in a non-modal way using Assets Library or AVFoundation?

  • martin

    ;;

  • Will

    Thanks for writing this class. I’m having an issue with the picker when loading lots of images. I have 1358 images in the camera roll and memory warnings are sent by the OS to my app. In the most severe case the picker crashes with a bad access in the preparePhotos method call.

    Could you investigate?

    Thanks,
    Will

  • Ayon Ashish

    Please update it for ios 5

blog comments powered by Disqus
canakkale canakkale canakkale balik tutma search canakkale vergi mevzuati