The most basic thing a user needs to do with a photo gallery application is
pick photos for a gallery. If you’re learning iPhone / iOS programming, you’ll
first discover the
class and think your job is done.
UIImagePickerController is fantastic for simple applications, but
it has one dealbreaker for a photo gallery application: It only lets you pick
one image at a time. If you are trying to create a gallery of multiple images,
this becomes tedious fast. Nobody will want to keep using your program.
There is a better way: Use the Assets Library APIs directly
The Assets Library gives you fine-grained control over accessing all media
that is available in the Photos application. It has only two drawbacks.
You have to write more code. A challenge, but not insurmountable.
The first time your program calls an
ALAssetsLibraryAPI directly, iOS prompts the user to inform him that your program wants to access location data. If the user does not consent, you don’t get access to pictures.
The reason for the prompt: Photos you take with the iPhone camera get geotagged. By reading the photos on the iPhone, you can make reasonable inferences about where the user has been.
UIImagePickerControlleravoids the prompt because it strips out all location metadata from the image before returning it to you. When you access
ALAssetsLibrarydirectly, however, you can read the location information. Thus the prompt. There’s no way around it. If you want to support multiple image picking in an iPhone / iPad application, you need to get the user’s consent for access to location information.
If you’re trying to decide if you should forego the convenience of
and start tapping into the power of
ALAssetsLibrary, the second drawback
is the one you should focus on. Users will likely find the “Location” prompt
scary, especially if they’re not expecting it. You’ll probably lose some people.
But on the flip side, there’s no other way to let your users pick multiple
images. Is it worth it?
If it is, get ready to buckle down and write some code. If you’re lazy,
you could copy the image picking code from Pholio. It’s about a dozen classes.
Better yet, you could copy
which is really a fantastic piece of work.
Pholio’s code, and I do use some of its algorithms & images. If all you need
is a way to pick multiple images from the iPad / iPhone galleries, then
you should just surf over to iCode blog and be done.
Pholio has an additional design goal, however. In addition to choosing photos
from the iPad library, I wanted to give users the power
to select photos from web sites, like Flickr. I set out to design a single
image picker that could handle multiple image sources. If your program needs
capabilities like this, then you’re going to be better off if you just design
your own image picker to meet your own needs. It’s more typing than just using
ELCImagePickerController, but it’s not that hard. The rest of
this article walks through the important design decisions for your picker
and how they were addressed in Pholio.
Invoking the Picker
It sounds silly, but the first decision you should make is how you want to
invoke your picker from your program. This decision creates the scaffolding
for the rest of your picker work. For Pholio, I decided I wanted one picker
class (creatively called
BDImagePickerController) that exposes exactly one
1 2 3 4 5 6 7 8 9
I think this is the simplest possible way to interact with an image picker.
It forces the image picker to appear in a
UIPopoverController (which is fine
as long as you target the iPad). You get the
UIPopoverController in case
you need to dismiss it yourself. (Remember, the iPad Human Interface Guidelines
say you must have no more than one popover controller visible at a time. If
you want to show a new one, dismiss the old one!) Then, if the user selects images,
the picker will call
imageBlock and give it the array of selected images.
No delegate protocol to conform to, no delegate property to forget to set;
everything can be written simply and inline like this (from
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Getting the Image Data
The image controller has called your block, and it’s given you an array of assets that the user has picked. Now what? How do you get the image data into the rest of your program – made more complicated if some assets need to get downloaded from a site like Flickr?
In Pholio, I addressed this problem by making each object in the
array (above) conform to the
BDSelectableAsset protocol. The protocol defines
a few methods, but the important one right now is:
This method codifies the next two design principles of Pholio’s image picker:
Getting images can take a long time, so don’t get them synchronously. Instead, get them asynchronously and provide a block to call when the image is ready.
Uncompressed images can be big. So instead of returning uncompressed image data, write the image to a file in the application documents folder (compressed as JPEG or PNG) and give the file name and type to the completion block.
So the caller of the image picker has a pretty easy job: Use the single
class method of
BDImagePickerController to present the picker, then
in the completion block get the image file using
But how does the picker itself work?
Supporting multiple image sources
The way Pholio’s picker supports multiple image sources is by defining two
roles: An asset source and an asset. An asset source simply knows how
to create an array of interesting assets that the user might want to choose
from. An asset knows how to produce thumbnails and image files on demand.
Assets also remember whether or not they have been selected. These two roles
are made concrete in two Objective-C protocols:
With these two protocols defined, I wrote one concrete class,
that knows how to get an array of assets from a
BDAssetsSource, display them,
let the user select and unselect assets, and finally return the results
to the caller. Then I wrote implementations of those protocols for the
Assets Library (
Putting it all together
At this point, you understand all of the building blocks of Pholio’s image
picker. Putting them together is easy. Inside
I create one table view that knows how to enumerate Assets Library groups
BDAssetsLibraryController) and one table view that knows how to enumerate
Flickr photosets (
IPFlickrSetPickerController). These controllers get wrapped
UINavigationController and a
UITabBarController. They push
BDAssetGroupController views on their navigation stacks.
Easy as pie!
As I said at the outset, if you want to support multiple image selection, be prepared to write code. But I hope this (and the corresponding source code) demystify the process of creating a custom image picker that supports multiple selection and supports multiple image sources.