October 7th, 2010 Posted by: Collin - posted under:Featured » Tutorials
Selecting an Asset (ALAsset)
The second table view we see is the AssetTablePicker. Here is the header to define that class
@interface AssetTablePicker : UITableViewController
{
ALAssetsGroup *assetGroup;
NSMutableArray *assets;
NSMutableArray *assetURLDictionaries;
int selectedAssets;
id parent;
NSOperationQueue *queue;
}
@property (nonatomic, retain) IBOutlet UILabel *selectedAssetsLabel;
@property (nonatomic, assign) id parent;
-(void)setAssetsGroup:(ALAssetsGroup*)_group;
-(IBAction)dismiss:(id)sender;
@end
|
This class is a bit more complicated than the album picker, but despite its complexity, its basic principals are similar. It has the same class properties as the AlbumPickerConrtroller with the addition of an array to keep track of the URL’s of all the Assets it encounters. Lets get into the main and see exactly what happen to fill this table. First in the view didLoad we do some house keeping with the navigation controller and call the method preparePhotos in the background. Since we are calling preparePhotos to run in the background the first thing we must do is declare an NSAutoReleasePool, at the end we will release it. This helps manage background operations within iOS. Next in prepare photos, we do the same operation as we did in AlbumPickerController except this time we add Assets to the array assets. The Assets object is instantiated with an ALAsset object and is a UIView subclass. It will be covered in greater detail later in this post. Every time we see an Asset we add its URL to the assetURLDictionaries array. This is due to accidental duplicates handed back from the Assets Library Framework. It is an odd workaround now, but seems to work fine. With that done we reload the table view.
@implementation AssetTablePicker
@synthesize parent;
@synthesize selectedAssetsLabel;
-(void)viewDidLoad {
[self.tableView setSeparatorColor:[UIColor clearColor]];
[self.tableView setAllowsSelection:NO];
UIBarButtonItem *doneButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismiss:)];
[self.navigationItem setRightBarButtonItem:doneButtonItem];
[self.navigationItem setTitle:@"Loading..."];
[self performSelectorInBackground:@selector(preparePhotos) withObject:nil];
}
- (void)setAssetsGroup:(ALAssetsGroup*)_group {
assetGroup = _group;
}
-(void)preparePhotos {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
void (^assetEnumerator)(struct ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
if(result != nil) {
if(![assetURLDictionaries containsObject:[result valueForProperty:ALAssetPropertyURLs]]) {
if(![[result valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypeVideo]) {
[assetURLDictionaries addObject:[result valueForProperty:ALAssetPropertyURLs]];
Asset *asset = [[Asset alloc] initWithAsset:result];
[assets addObject:asset];
}
}
}
};
assets = [[NSMutableArray alloc] init];
assetURLDictionaries = [[NSMutableArray alloc] init];
[assetGroup enumerateAssetsUsingBlock:assetEnumerator];
[self.tableView reloadData];
[self.navigationItem setTitle:@"Pick Photos"];
[pool release];
}
|
So now we are at a point where we have an array (assets) of all the ALAssets in the selected ALAssetGroup represented as Assets objects. We now have to figure out a way to display thumbnails of each of these images (4 per row) in a table view. We will accomplish this be creating a custom UITableViewCell that we will call AssetCell. An asset cell will accept an array of Asset objects and utilize it as the UIView subclass that it is to lay them out. We will get to that in the next section, but it is important to know now as we need to create the method that will prepare the subarray of Assets that we will pass to our AssetCell cells. This method is called assetsForIndexPath:. The method will look at the index path and then convert it to the indices of the actual ALAsset objects the index path will be displaying. After it has done this it checks that it is not going to look for more assets then there are in the array. This method will always return an array of size 4 except for the last row of the table view which will be either 1, 2, 3 or 4 assets in length. See the code below.
-(NSArray*)assetsForIndexPath:(NSIndexPath*)_indexPath {
int index = (_indexPath.row*4);
int maxIndex = (_indexPath.row*4+3);
if(maxIndex < [assets count]) {
return [NSArray arrayWithObjects:[assets objectAtIndex:index],
[assets objectAtIndex:index+1],
[assets objectAtIndex:index+2],
[assets objectAtIndex:index+3],
nil];
}
else if(maxIndex-1 < [assets count]) {
return [NSArray arrayWithObjects:[assets objectAtIndex:index],
[assets objectAtIndex:index+1],
[assets objectAtIndex:index+2],
nil];
}
else if(maxIndex-2 < [assets count]) {
return [NSArray arrayWithObjects:[assets objectAtIndex:index],
[assets objectAtIndex:index+1],
nil];
}
else if(maxIndex-3 < [assets count]) {
return [NSArray arrayWithObject:[assets objectAtIndex:index]];
}
return nil;
}
|
With this convenience method in place we can move into the UITableViewDataSource methods for this tableview. The way in which we get it to look so unlike a normal tableview will become clear when we look into the implementation of the AssetCell UITableViewCell and the Asset UIView. For now we will just inject the AssetCells with the arrays provided by our assetsForIndexPath method.
#pragma mark UITableViewDataSource Delegate Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return ceil([assets count]/4.0);
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
AssetCell *cell = (AssetCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[AssetCell alloc] initWithAssets:[self assetsForIndexPath:indexPath] reuseIdentifier:CellIdentifier] autorelease];
}
else {
[cell setAssets:[self assetsForIndexPath:indexPath]];
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 79;
}
- (void)dealloc {
[super dealloc];
}
|
The final method contained within our AssetTablePicker is a method used when the user has selected all the assets that they want. This method looks through the Assets we have within our assets array. An Asset object has a class method called selected which returns a BOOL. If the Asset has been selected this will return true. We make a subarray of all the selected Asset object, bundle them up and pass them to our parent (AlbumPickerController). This will become clearer near the end of the post. See the code for the dismiss method below.
-(IBAction)dismiss:(id)sender {
NSMutableArray *selectedAssetsImages = [[NSMutableArray alloc] init];
for(Asset *asset in assets) {
if([asset selected]) {
[selectedAssetsImages addObject:[asset asset]];
}
}
[(AlbumPickerController*)parent selectedAssets:[NSArray arrayWithArray:selectedAssetsImages]];
}
|
Displaying Assets using AssetCell
The next class we encounter is AssetCell, a UITableViewCell subclass. The header and main can be see below. The only interesting code within this class is the code to layout each of the assets located in layout subviews. This method goes through every Asset object which it was handed and applies a frame to it. The UIImagePickerController has a 4 pixel border between each of the assets it lists. We do the same and smartly calculate the frame for each asset as the for loop executes. Also within this loop we create a UITapGestureRecognizer for each Asset object. We essentially tell each asset view to call the method toggleSelection: on itself every time it is tapped. You will see the implementation of the toggleSelection method in a moment. For now see the code below for specifics on AssetCell.
AssetCell.h
@interface AssetCell : UITableViewCell
{
NSArray *assets;
}
-(id)initWithAssets:(NSArray*)_assets reuseIdentifier:(NSString*)_identifier;
-(void)setAssets:(NSArray*)_assets;
@end
|
AssetCell.m
@implementation AssetCell
-(id)initWithAssets:(NSArray*)_assets reuseIdentifier:(NSString*)_identifier {
if(self = [super initWithStyle:UITableViewStylePlain reuseIdentifier:_identifier]) {
assets = _assets;
[assets retain];
}
return self;
}
-(void)setAssets:(NSArray*)_assets {
for(UIView *view in [self subviews]) {
[view removeFromSuperview];
}
assets = nil;
assets = _assets;
[assets retain];
}
-(void)layoutSubviews {
CGRect frame = CGRectMake(4, 2, 75, 75);
for(Asset *asset in assets) {
[asset setFrame:frame];
[asset addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:asset action:@selector(toggleSelection)]];
[self addSubview:asset];
frame.origin.x = frame.origin.x + frame.size.width + 4;
[asset release];
}
}
-(void)dealloc {
[assets release];
[super dealloc];
}
|
Pages: 1 2 3 4