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 at 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 one photo at a time, and even in Apple's photo app, where you can choose several pictures at a time, you can not use multitouch 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 will not 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 Sun
ELCImagePickerController * controller = [[ELCImagePickerController alloc] init image picker]; [Controller setDelegate: self]; [Self presentModalViewController: controller animated: YES]; [Release];
The ELCImagePickerController will return select the 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:]
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 init method and present the image picker 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.
ELCImagePickerController.m
The other callback methods (cancelImagePicker and selectedAssets :) will be discussed later in the post
Selecting an album (ALAssetsGroup)
The first table view we see is the album Picker controller. Here is the header to define that class
In the main we first fill in viewDidLoad. We first set the navigation controller to "Loading". Next we create an NSOperation queue Which calls to surgery on preparePhotos. The preparePhotos method is where we will ask the Asset Library Framework for the list of album I can get access from. Finally I add the dismiss button to the navigation bar. Use the code below.
Next we are going to define the method dismiss. This will call the method on cancelImagePicker out parent (ELCImagePickerController). We now get to the preparePhotos method. The first thing we do in this method is declare a block that we call assetGroupEnumerator. It takes at ALAssetGroup and a BOOL as parameters.The block checks the ALAssetGroup against nil and adds it to the NSArray class parameter asset groups. At the end of the block I reload the table view. This is the block that I will use to enumerate through all the ALAssetGroups that the Assets Library framework gives access to. After defining the block we instantiate the asset groups array. Next we create at ALAssetsLibrary object. We call the method enumerateGroupsWithTypes: using blocks: filureBlocks: passing assetGroupEnumberator in the block we declared at the beginning of the method.We create a small block withing the method as the failure blocks whichwill be called upon error. See the code below.
Next we define the method that will be called when assets have been selected. This, like the dismiss button, makes a call up to our parent with the selected assets.
Next we define the methods to fill and control the table view for our class. We will make a method to reload the tableView. The tableView will only have one section, and it will have as many rows as there are objects in the asset group array. For each row we will set the text to the name of the album, followed by the number of photos within the album. Next we will set the image view of the cell to the conveniently provided poster image that is provided by ALAssetGroups. We make the cells a bit taller and we set the accessory to a Disclosure Indicator. Finally we define the method to handle when a cell is tapped. We will create. At instance of our next tableView (Table Asset Picker), pass the selected ALAssetGroup into it and push it onto the navigation stack The rest of the album picker controller class can be seen below.
The second table view we see is the Asset table picker. Here is the header to define that class
This class is a bit more complicated than the album picker, but despite its complexity, its basic principals are similar. It has the same properties as the class with the addition of AlbumPickerConrtroller an array to keep track of the URLs 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 to declare NSAutoreleasePool, at the end we will release it. This helps manage background operations within iOS. Next to prepare photos, we do the same as we did in Operation album picker controller except this time we add assets to the array assets.The asset object is instantiated with an object and ALAsset 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.
So now we are at a point where we have an array (assets) of all the ALAssets ALAssetGroup Represented in the selected objects as assets. We now have to figure out a way to display thumbnails of each of these images (4 per row). In a tableViewWe will accomplish this be creating a custom UITableViewCell that we will call Asset Cell. 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 asset Cell cells. This method is called assetsForIndexPath. The method will look at the index path and then convert it to the indices of the actual objects ALAsset 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 whichwill be either 1, 2, 3 or 4 assets in length See the code below.
With this convenience method in place we can move into the UITableViewDataSource methods for this tableView. # In which the way we get it to look so unlike a normal table view will become clear when we look into the implementation of the asset and the asset Cell UITableViewCell UIView. For now we will just inject the asset Cells with the arrays provided by our assetsForIndexPath method.
The final method contained within our Asset Table Picker 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 (album picker controller). This will become clearer near the end of the post the code for the lake dismiss method below.
Displaying assets using Asset Cell
The next class we encounter is asset Cell, a UITableViewCell subclass. The main header and 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 object Asset 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.So within this loop we create a UITapGestureRecognizer for each asset object.Essentially we 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 asset Cell.
AssetCell.h
AssetCell.m
Asset is the final class Which Comprises the ELCImagePickerController. Asset is a UIView subclass Which takes in to ALAsset and creates a view that represents it. The final view will be a square thumbnail of the ALAsset that is passed in along with a check mark overlay that is very similar to the check mark used in Apple's Photos application. In Asset Cell we set up the UITapGestureRecognizer that wants to call this method whenever asset is tapped. ALAssets, Which We inject into. Asset to object when creating, have a convenience method called thumbnail that we can utilize to get the actual image that will represent the asset After adding to the view that we add the image view for the overlay and set it as hidden. Our toggleOverlay: method will check Whether that view is hidden. Finally we have a checker to see if the asset is selected with the method selected. If the overview is the hidden asset is not selected and if it is visible it is selected. All this comes together to form the final smarts we need to make our picker functional. See the header and main below.
Asset.h
Asset.m
Passing the Assets Back Up
So now that all the assets can be displayed and selected lets take a look at how they are passed up the chain. First thing to know is that the ELCImagePickerController passes up asset objects, since they contain references to the ALAsset Which they represent. With that said the place we start is in the Asset Table Picker. When I dismiss the class there is clicked dismiss calls the method which looks through the array and checks for assets which one is selected. It adds the selected asset objects to an array and then calls album picker controller selectedAssets: method. The selectedAssets method just passes along this array to the method ELCImagePickerController selectedAssets. Here a dictionary is constructed to represent every asset that was selected. The dictionary contains the same information that is delivered by UIImagePickerController when it delivers assets.This way the ELCImagePickerController can be included into projects as simply as possible. Below you will see each of the passback methods Described above as well as the definition of the ELCImagePickerControllerDelegate Which protocol defines the methods that should be implemented on the calling class.
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.
No comments:
Post a Comment