2011年11月16日星期三

iOS5: Saving photos in custom photo album

iOS5: Saving photos in custom photo album (+category for download)

Custom photo albums in iOS5

iOS5 features new APIs in the AssetsLibrary framework which give you the opportunity to manage your app’s own photo album inside the user’s photo library. That is a logical step from Apple since photo applications market is booming and users want to have their photos organized by the application they were created with. (I released 4 photo apps to date, 5th in review – so I have pretty good overview on photo apps)
I wanted to include a ALAssetsLibrary API chapter in “iOS5 by Tutorials” but I was experience problems with the assets framework until the very launch of iOS5 (and after) so I decided not to include such chapter in the book. It looks like the combination of the iCloud photo stream, Core Data managed photo library and 3rd party apps read/write access at the same time is giving Apple hard time – I’m looking forward to 5.0.1 to see if the problems would be fixed.
Without further ado – iOS5 allows you to create and delete albums in the user photo library and add pictures from the camera roll to those albums too. Let’s have a look.

Custom photo album category for ALAssetsLibrary

Unfortunately browsing through the new assets APIs you won’t find a single method, which will create or access your custom album and save an image inside. And honestly this is all I would need when creating a photo app – something easy to call and pass a UIImage to.
So while creating my latest app (Fun Photo Booth – to come out in the next days) I crafted a simple category add-on for ALAssetsLibrary and in this tutorial I’ll show you how to use for your own photo apps (free code download included of course)
The method we will use from the category is the following:
-(void)saveImage:(UIImage*)image
         toAlbum:(NSString*)albumName
withCompletionBlock:(SaveImageCompletion)completionBlock;
Easy peasy. Your app should create a UIImage by doing its special magic and will just need to pass it to this method along with the album name and a block to handle the end of the saving process. I think this should be as simple as it gets. Let’s see this in action by creating a simple project and use the category.

Building a demo with own photo album

Start by creating a single view project in Xcode – name it “CustomPhotoAlbumDemo”. Add the AssetsLibrary.framework to the project (I’m going quicker over these steps ok?) Download the ALAssetsLibrary+CustomPhotoAlbum code from here: ALAssetsLibrary_CustomPhotoAlbum.zip and copy the 2 files for the category inside your Xcode project. Setup is done.
Now open up the XIB file in the project and setup a single button like so:

Open up your view controller header file and add this import at the top:
#import <AssetsLibrary/AssetsLibrary.h>
and a property to keep hold of the assets library:
@property (strong, atomic) ALAssetsLibrary* library;
last you’ll need the class to be a UIImagePickerController delegate, so after “UIViewController” on the same line add:
<UIImagePickerControllerDelegate>
That should be about right. Open the implementation file now. Just below the @implementation line synthesize the library property:
@synthesize library;
NB! When you work with ALAssetsLibrary it represents a snapshot of the assets on the device, i.e. if you have different instances of ALAssetsLibrary they may get out of sync, not to mention other horrible discrepancies, which seems to happen on iOS5. So, advice in Apple dev forums is – have a single instance of the library and make it a strong property, make sure you never loose it and work only with it. I find this advice immensely helpful, although it doesn’t really solve all problems, but does most
So, we have the library in a property – let’s also take care of initializing it. Lookup viewDidLoad and viewDidUnload and replace them with:
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.library = [[ALAssetsLibrary alloc] init];
}
 
- (void)viewDidUnload
{
    self.library = nil;
    [super viewDidUnload];
}
Few more steps and we’ll be ready with our photo app demo. I hope you are familiar with using the camera controller, as I won’t go into details. In short – you make an instance of a controller, which basically takes a photo for you and serves it back to a delegate of your choice. So add the method to take the photo:
- (IBAction)takePhoto
{
    UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
    imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
 imagePickerController.editing = YES;
    imagePickerController.delegate = (id)self;
 
    [self presentModalViewController:imagePickerController animated:YES];
}
OK. Let’s connect the UI with the code. Open the XIB file again. While holding the “Ctrl” key drag with the mouse from the button to File’s Owner and from the popup menu choose “takePhoto”:

OK. If you want you can run the app and see if the Camera interface pops up when you tap the button (That won’t work in the Simulator, give it a try on the device)
Now when the user takes a photo and decides to use it, the “imagePickerController:didFinishPickingImage:editingInfo:” method on your view controller (or other delegate) will be called. So you will get a UIImage instance, do your magic on it (if any) and then proceed to saving. In our demo there’ll be no magic applied to the image, so we will just save in a custom photo album the photo taken. (If you want to learn more about Image filters and such with the new for iOS5 CoreImage, have a look at the “iOS5 by Tutorials” book)
Let’s implement the callback:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo
{
    [self.library saveImage:image toAlbum:@"Touch Code Magazine" withCompletionBlock:^(NSError *error) {
        if (error!=nil) {
            NSLog(@"Big error: %@", [error description]);
        }
    }];
 
    [picker dismissModalViewControllerAnimated:NO];
}
So this is the category method, which takes in a UIImage, the name of the album and a completion block. The album may or may not exist already. If it does not, it will be created on the fly (yay). And the completion block will receive an NSError instance if there was an error in any step of the process.
That’s all about saving to a custom photo album with the ALAssetsLibrary+CustomPhotoAlbum category. If you’re interested have a look at the category code to see what’s going on behind the scenes.
Now to wrap up with the demo project code, let’s just add this one method for when the user cancels the camera UI:
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    [picker dismissModalViewControllerAnimated:NO];
}
Let’s run the project; initially I have 2 albums in my photo library:

Then I take a photo by tapping “Take photo” in the app:

And after the camera UI is gone, let’s switch again to the Photos app on the device:

Now, you can also play further with the new AssetsLibrary APIs – you can now edit files inside your own albums (yes, you read correct, not open and save copies, but edit files – though only the ones your app did create), but the category at hand should cover the basic needs of any photo app.
Here’s the complete Xcode project of the demo app: CustomAlbumDemo (it includes the category source code files)
I hope that’ll be helpful to you and also do let me know what other iOS5 features are you interested to read about.
Marin
Marin Todorov
is an independent iOS developer and publisher. He's got more than 18 years of experience in a dozen of languages and platforms. This is his writing project.
» Marin's homepage    » Contact    » Marin's Cocos2D game creation course

没有评论:

发表评论