iOS 4 presented a million billion new API’s by Apple’s count, but for this post I am going to show a quick demo of the new Assets Library API where you can now get to users photos and videos with more access than ever before. This new API relies on the new programming device called Blocks that has been introduced with iOS 4. Blocks are used through many of the new API’s as a kind of extended @selector. We will look into this new development device and make a small project to create our own UIImagePickerController. In the end we are going to create a tableview that is filled with all the photos within our library. Check out the video below or follow the steps typed out below.
Screencast
Using the Assets Library API in iOS 4 from Collin Ruffenach on Vimeo.
Creating the Project
Create a new project in xCode. Make it for the iPhone since this is an iOS 4 framework. A view based application will create a view controller for us, so lets use that. Call your project MyImagePicker.
Adding in the framework
This project will revolve around taking advantage of the AssetsLibrary framework introduced with iOS 4. Due to this you must have the newest xCode and iPhone SDK installed with iOS 4.0 support. If this is done you should be able to expand the frameworks folder of your project. Right click on UIKit.framework and select “Reveal in Finder”. This will open up the folder that contains all of the frameworks you can use in an iOS 4 project. Find AssetsLibrary.framework and drag into the Frameworks folder of your project. Once dragged in make sure that the Copy items into destination folder options is deselected and that it is set to recursively create groups for any added folders.
Using the framework with blocks
iOS 4.0 introduces a totally new programming concept for Objective C developers to take advantage of when creating their own code and when using Apple’s API. Blocks in the most simple terms are Objective C objects that are methods. A block uses the special character ^ to denote its beginning and is a required parameter of many of the API’s introduced within iOS 4.0. Apple has some great documentation on blocks, and how they conceptually fit into the current objective C landscape. You can see their concise but effective overview here. For our purposes I am just going to review the simple syntax of a block.
Before we dive into blocks lets take a look at the AssetsLibrary framework methods that will require them. The general flow of the object access we will be doing is as follows.
We are going to create an ALAssetsLibrary and call a method on it that will enumerate through all the ALAssetsGroups it has access to. For every ALAssetsGroup we will call a method on it which will enumerate through the ALAssets it has access to. We will save each ALAsset into an array we will use to populate our tableview. The two different enumerations we are going to perform is where the blocks will come in. But lets first set up our view controller appropriately.
MyImagePickerViewController.h
#import <UIKit/UIKit.h>; #import <AssetsLibrary/AssetsLibrary.h>;
@interface MyImagePickerViewController : UIViewController { IBOutlet UITableView *tableview; IBOutlet UIActivityIndicatorView *activity; NSMutableArray *assets; } @property (nonatomic, retain) IBOutlet UITableView *tableview; @property (nonatomic, retain) IBOutlet UIActivityIndicatorView *activity; @end
We are going be using the assets array here to hold the assets that we are going to pull using the AssetsLibrary Framework.
Now open up MyImagePickerViewController.xib and drag in a UITableView and a UIActivityIndicatorView. With these in place connect them to the IBOutlets you created for this class. Also make sure to connect the UITableViewDataSource and UITableViewDelegate to the MyImagePickerViewController as well. With that done we can start to use the AssetsLibrary Framework.
Using the Asset Library
Now we are going to use our AssetsLibrary framework. This will be done within the viewDidLoad method of your MyImagePickerViewController.m. Put the following code in there.
1 - (void)viewDidLoad { 2 3 [super viewDidLoad]; 4 [activity startAnimating]; 5 6 void (^assetEnumerator)(struct ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop) { 7 if(result != NULL) { 8 NSLog(@"See Asset: %@", result); 9 [assets addObject:result]; 10 11 } 12 }; 13 14 void (^assetGroupEnumerator)(struct ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) { 15 if(group != nil) { 16 [group enumerateAssetsUsingBlock:assetEnumerator]; 17 } 18 19 20 [self.tableView reloadData]; 21 [self.activity stopAnimating]; 22 [self.activity setHidden:YES]; 23 }; 24 25 assets = [[NSMutableArray alloc] init]; 26 library = [[ALAssetsLibrary alloc] init]; 27 [library enumerateGroupsWithTypes:ALAssetsGroupAlbum 28 usingBlock:assetGroupEnumerator 29 failureBlock: ^(NSError *error) { 30 NSLog(@"Failure"); 31 }]; 32 }
We are going to be going though this code line by line. On line 4, we start animating our activity indicator. I will explain why we have to do that a little bit later. Lines 6 – 23 are the lines that will declare the two blocks that we will use to fill our tableview with pictures. We are going to declare two blocks to use as parameters to methods within a ALAssetLibrary object and a ALAssetGroup object. The first method call we will make will be on our ALAssetLibrary object. So for the moment lets skip passed the block declarations and look at line 25. On line 25 we instantiate our NSMutableArray to hold the ALAssets that we will pull out from the AssetsLibrary. On line 26 we will create our ALAssetsLibrary object. We will call a single method on this object to loop through all of the assets in a library. The method is:
– enumerateGroupsWithTypes:usingBlock:failureBlock:
This method takes in 3 parameters. An ALAssetGroup type, a block to be performed on each group and a block to be performed on failure. Lets talk about block syntax. A block is a native Objective C object. It is a subclass of NSObject. With that said it is also an Objective C method. An objective C method is composed of three things. A return type, a parameter list and a piece of code to be executed. We are going to pass the assetsGroupEnumerator block object that declare on line 14 into this method. Lets take a look at how the block is declared.
You begin by specifying a return type for the block. Void in this case. Next you enclose a name for the block which is proceeded by a carrot ^ character. This is the special character Apple has decided on to denote blocks. You begin block names with blocks as well as the actual declaration of what a block is. This name is wrapped in parenthesis. This is followed by the parameter list that the block will accept. In this case it is a ALAssetsGroup object and a Boolean indicating whether to stop. Now I did not come up with these parameters by myself. Since I will be passing this block into an ASAssetLibrary the documentation will tell us what will be passed into the block. ALAssetsLibrary objects documentation can be seen here. With that all said, lets take a look at the code that will actually compose this block. Starting on line 15 we will make sure that we are seeing a valid group. If the ALAssetsGroup object that we are passed is valid then we will call another enumeration on the group which will enumerate through assets. This enumeration method also requires a block. We declared this block above. This block is called assetEnumerator. Once again we retrieved the parameters list from the documentation of the object asking for, in this case an ALAssetsGroup.
For this block, which begins on line 6, the block is passed an ALAsset object, an index and a stopping condition boolean. Within the code of the block we will ensure that the returned ALAsset is valid and if it is we will add it to our array which will hold all of our assets. With this block declared we pass it into the call to our ALAssetsLibrary object on line 16. Once every group has been enumerated and our array is filled with all of our ALAssets we will tell the tableview to reload its data and get rid of our UIActivityIndicatorView.
With all of this done all that remains if filling in the required UITableViewDataSource methods. The tableview will be filled with the assets we collected. ALAsset objects are cool in that they include a method called -thumbnail which returns a CGImageRef to a thumbnail we will use. I won’t explain the development of these methods any further since they are pretty straight forward.
// Customize the number of sections in the table view. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } // Customize the number of rows in the table view. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [assets count]; } // Customize the appearance of table view cells. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } ALAsset *asset = [assets objectAtIndex:indexPath.row]; [cell.imageView setImage:[UIImage imageWithCGImage:[asset thumbnail]]]; [cell.textLabel setText:[NSString stringWithFormat:@"Photo %d", indexPath.row+1]]; return cell; }