Do you have an idea for an app but lack the programming knowledge to begin building it? In this weekly blog series, How to Unleash Your Inner App Developer, I will take you, the non-programmer, step by step through the process of creating apps for the iPhone, iPod touch, and iPad. Join me each week on this adventure, and you will experience how fun turning your ideas into reality can be! This is Part 11 of the series. If you're just getting started now, check out the beginning of the series here.
Now that you have some of the basics of Objective-C programming under your belt, it's time to dive into some deeper coding territory. However, first let's talk about the homework assignment I gave you last week. I asked you to enhance iAppsReview so that it pulled the name of the app from the Write Review scene's text field.
This required creating an outlet for the text field and using the NSString class to append two strings together. For a video demonstrating how to do this, check out this link.
To get the latest version of iAppsReview, including my previous post's homework assignment, select this link. If you run into trouble while following the steps below, you can check out the video at this link to watch me perform the steps for you.
The iOS Photo Library
Apple allows the custom apps you create to access the Photo Library on an iOS device. This allows you to add some great functionality to your apps. Apple makes it easy to do so by providing a pre-built user interface allowing users to navigate their photos and select an image. Our goal in this post is to display an image picker when the user taps the Photo Album button (Figure 1), let them select an image from the photo album, and display a thumbnail of the image in the image view control to the right of the button.
|Figure 1 - The Photo Album button in the bottom-left corner of the Write Review scene|
Adding Photos to the Simulator
By default, the iOS Simulator doesn't have any photos in its Photo Library, which we need for this demo. Fortunately, it's easy to add images as outlined in these steps:
- Open the iAppsReview project in Xcode;
- Press Xcode's Run button to start the Simulator;
- Download the Doodle Jump app images I have created for you from this link. This automatically decompresses the DoodleJump folder into your Mac's Downloads folder;
- Open Finder and Navigate to your Downloads folder. Expand the DoodleJump folder and you will see the DoodleJump1.PNG and DoodleJump2.PNG files;
- Drag the DoodleJump1.PNG file from the Finder window to the Simulator as shown in Figure 2, and then release your mouse button.
|Figure 2 - Dragging an image to the Simulator adds it to the Simulator's Photo Library.|
When you do this, Safari is launched in the Simulator, and the image is displayed in the browser window as shown in Figure 3;
|Figure 3 - The image is displayed in Safari.|
- Next, click on the image and hold your mouse button down until a panel slides up from the bottom and then click the Save Image button.
- Now drag and drop the DoodleJump2.PNG from Finder onto the Simulator and repeat the same actions in the previous step to save the image.
That's it! Now you have two images in the iPhone Simulator you can access from the iAppsReview app. If you'd like, you can click the Simulator Home button, launch the Photos app, and check out the two newly added images.
Creating an Action Method and Outlet
In order to display the image picker view that allows the user to select an image from the Photo Album and store it in the image view, you need to do the following:
- Create an accessPhotoLibrary: action method for the Photo Album button;
- Create an outlet named imgThumbNail for the image view.
Based on what you have already learned in previous posts, see if you can do this on your own. When you're finished, check out the video at this link to see how you did.
Implementing the Action Method
Now that you have created the accessPhotoLibrary: action method, it's time implement the method by adding code to it.
- If the Assistant Editor is still open, close it by clicking the button on the left button in the Editor button group at the top of the Xcode window;
- In the Project Navigator, click the WriteReviewViewController.m implementation file and scroll down to the bottom of the code file to see the accessPhotoLibrary: method you just created;
- Add the code shown in Figure 4, and then we'll discuss what the code does (based on my previous posts, I'm assuming you know the basics of how to type code into a method. Make sure you allow Xcode's Code Completion to automatically complete your code rather than typing in every character yourself.)
|Figure 4 - accessPhotoLibrary: implementation code|
The first line of code creates an image picker controller object from the UIImagePickerController class. In the second line of code, the image picker controller object's sourceType property is set. The third line of code displays the image picker controller.
Let's take a closer look at the second line of code. Whenever you set the value of a property you're not familiar with, it's a good idea to read Apple's documentation to see how the property is used. Figure 5 contains the documentation for the sourceType property.
|Figure 5 - Apple's documentation for the sourceType property of the UIImagePickerController|
The documentation specifies that the sourceType property holds a value of the type UIImagePickerControllerSourceType, and it refers to the type as an enumeration. We'll find out what that is in just a moment.
If you click on the UIImagePickerControllerSourceType link, it takes you to the documentation shown in Figure 6.
|Figure 6 - The UIImagePickerControllerSourceType enumeration|
An enumeration is a group of related constant values. In this case, the UIImagePickerControllerSourceType enumeration defines the valid values that can be stored in the sourceType property. Enumerations come in handy in cases like this when there are a finite number of valid values that can be stored in a property. Unlike string variables where you can store any string of characters, or integer variables where you can store any valid integer, enumerations limit the valid values to a finite set.
Reading through the documentation, we can determine that the value we want is UIImagePickerControllerSourceTypePhotoLibrary because it specifies that the device's photo library is the source for the image picker. This is exactly what we want.
Before moving on, take a look at Figure 7, which contains a sequence diagram that shows the interaction between the WriteReviewViewController and UIImagePickerController objects.
|Figure 7 - The message interaction between the writeReviewController and imagePicker objects|
The objects at the top of the sequence diagram represent the objects that are created from the WriteReviewViewController and UIImagePickerController classes at run time. The dotted lines below the objects are called "lifelines" and indicate the life of an object. The arrows pointing between the objects and between the lifelines are each a single message passed between the objects. Here's the order in which the messages occur:
- The writeReviewViewController object creates an imagePicker object from the UIImagePickerController class;
- The writeReviewViewController object passes a setSourceType: message to the imagePicker object, passing a UIImagePickerControllerSourceTypePhotoLibrary argument. When you set a property value, behind the scenes, a method is actually executed as indicated here;
- The writeReviewViewController object sends a presentViewController: message to itself, passing the imagePicker object as an argument (I've left out the other arguments for the sake of brevity). When the presentViewController: method is executed at run time, the imagePicker object is displayed in the user interface.
I find it's important to visual object interaction so you truly understand how your code works. As you work on your own apps, I recommend creating sequence diagrams for your app's more complicated message sequences.
Testing the Photo Library Integration
Let's take the code we wrote for a spin to see how it works.
- In Xcode, click the Run button;
- When the app appears in the Simulator, click the Write a Review option, and then on the Write Review screen, click the Photo Library button. This displays the Photos screen shown on the left side of Figure 8, which indicates that we have two saved photos. Exactly what we expected. This is the same interface that users are comfortable using in the built-in Photos app;
|Figure 8 - The Photo Library screens of the Image Picker Controller|
- Tap the Saved Photos row and you will be taken to the Saved Photos screen shown in the right side of Figure 8. If you select one of the photos, the image picker controller disappears down through the bottom of the screen. Nothing happens with the image is selected because we haven't written any code to respond to the selection yet.
Retrieving the Selected Image Using a Delegate
If you take another look at the sequence diagram in Figure 7, there doesn't seem to be a way for the WriteReviewViewController object to get the image that is selected in the imagePicker object.
If you read further down in the Apple's documentation for the UIImagePickerController class, you will see the section Providing a Delegate Object as shown in Figure 9.
|Figure 9 - You must provide a delegate object for an image picker controller.|
This documentation states: "To use an image picker controller you must provide a delegate that conforms to the UIImagePickerControllerDelegate protocol." What does this mean?
In this context, delegation refers to one object relying on another object (the delegate) to provide a specified set of functionality. In Objective-C, you can define a specific set of required functionality using a protocol. The fact that the protocol mentioned in the documentation is named UIImagePickerControllerDelegate indicates it's the image picker controller that specifies the functionality it needs to pass back information about the image selected by the user.
If you click on the UIImagePickerControllerDelegate Protocol Reference link in the help topic shown in Figure 9, you are taken to that help topic that defines the methods shown in Figure 10.
|Figure 10 - The UIImagePickerControllerDelegate Protocol methods|
We can ignore the third method in the list, because the documentation tells us it is "deprecated". This is a status that is applied to methods in the Cocoa Touch Framework that should be avoided because they have been superseded by other methods.
Here's a synopsis of the first two methods:
- imagePickerController:didFinishPickingMediaWithInfo: The image picker controller calls this method on the delegate object when the user has finished selecting an image. It passes back information to the delegate describing the media that has been selected;
- imagePickerControllerDidCancel: The image picker controller calls this method on the delegate object when the user clicks Cancel rather than selecting an image.
Ultimately, any object that is the delegate of the image picker controller must have these two methods. Unfortunately, Apple's documentation is somewhat misleading. While it's true that a delegate object must implement the UIImagePickerControllerDelegate, it must also implement the UINavigationControllerDelegate. How do I know this? The UIImagePickerController class has a delegate property that is described in the Apple documentation shown in Figure 11.
|Figure 11 - The delegate property of the UIImagePickerControllerDelegate class|
So bottom line, we need to implement both of these protocols on our delegate object.
Although you can make any object the delegate for the image picker controller, it's typical to make the view controller the delegate object. Let's do that now as outlined in the following steps.
- In the Project Navigator, select the WriteReviewViewController.h header file. We are selecting the header file because this is where you publicly declare (for other classes to see) that the class implements a particular protocol;
- Near the top of the header file, click to the right of the line that begins with @interface;
- We could put the code that implements the protocol on this same line, but it's easier to read if we add it on a second line. To do this, press return and then tab to indent the code a bit;
- Next, type the code shown in Figure 12. This code between angled brackets tells the world (because it's in the public header file) that the WriteReviewViewController class implements the UIImagePickerControllerDelegate and UINavigationControllerDelegate protocols;
|Figure 12 - Implementing the protocols|
- Before we implement the methods that are in the UIImagePickerControllerDelegate protocol, we need to add some variables to the class to hold some of the information that we get back from the image picker controller. Rather than creating local variables within a method, we are going to create variables at the class level. This type of variable is known as an instance variable. This type of variable is very useful, because, unlike local variables, they can be accessed by any method in the class.
To create these instance variables, go to the Project Navigator and select the WriteReviewViewController.m implementation file. Afterwards, scroll to the top of the code file and locate the line of code that begins with the key word @implementation. Click to the right of this line of code, press the space bar, and add the code between curly braces shown in Figure 13.
|Figure 13 - Declaring instance variables|
This code declares an instance variable named image of the type UIImage and another instance variable named imageURL that holds an object of type NSURL;
- So, now we need to implement the methods that are in the UIImagePickerControllerDelegate protocol. To do this, add the methods shown in Figure 14.
Figure 14 - The implementation of the UIImagePickerControllerDelegate methods
The code in the first method sends a dismissViewControllerAnimated:completion: message to itself, which hides the image picker controller after the user selects an image. Afterwards it grabs the image from the info parameter passed back from the image picker controller and stores it in the image instance variable for future use. Next, the code gets the URL of the image (the location of the image on the iOS device) and stores it in the imageURL instance variable, also for future use.
The imagePickerControllerDidCancel: method also dismisses the image picker controller, but doesn't need to do anything afterwards;
- Now there's just one other thing you need to do—set WriteReviewViewController as the delegate for the image picker controller. To do this, add the code shown in Figure 15 to the accessPhotoLibrary: method.
|Figure 15 - Store a reference to the WriteReviewViewController object in the delegate property of the image picker controller.|
To get a clearer picture of how these new messages between the objects work, check out Figure 16.
|Figure 16 - The complete message flow between objects|
In this updated sequence diagram, notice there is a new setDelegate message where the writeReviewViewController object stores a reference to itself in the imagePicker object's delegate property. At the bottom of the diagram, a new message has been added. This message is sent from the imagePicker object to the writeReviewViewController object after the user selects an image. A media argument is sent in this message that contains a collection of objects containing information about the selected image.
Testing the Delegate Code
Now you're ready to test the code and see how it works.
- Click Xcode's Run button;
- When the app appears in the Simulator, click the Write a Review row;
- Click the Photo Album button;
- In the Photos screen, select the Saved Photos row;
- Select either of the two images;
- The image picker controller should disappear and you should see the image displayed as a thumbnail in the image view as shown in Figure 17.
|Figure 17 - The selected photo appears in the image view.|
If you would like a copy of the project as we have completed it so far, just click on this link.
Personally, I find it interesting and fun to add the type of functionality we have added to iAppsReview in this post. Along the way, you have learned a number of new concepts including delegates, protocols, and instance variables. As time goes by and we use these concepts in future posts, they will become far more familiar and an important part of your iOS tool kit.