Adding an Image View
In this exercise, our view creation will begin with the most important object of the project: the image view (UIImageView). Open the Interface Builder Objects Library and drag an image view into the view window.
Because the view is has no
images assigned, it will be represented by a light-gray rectangle. Use
the resize handles on the rectangle to size it to fit in the upper
two-thirds of the interface (see Figure 2).
Setting the Default Image
There are very few
attributes for configuring the functionality of an image view. In fact,
there is only one: the image that is going to be displayed. Select the
image view and press Command+1 to open the Attributes Inspector (see Figure 3).
Using the Image drop-down
menu, choose one of the image resources available. This will be the
image that is shown before the animation runs, so using the first frame
(frame-1.png) is a good choice.
By the Way
What about the animation? Isn’t
this just a frame? Yes, if we don’t do anything else, the image view
will show a single static image. To display an animation, we need to
create an array with all the frames and supply it programmatically to
the image view object. We do this in a few minutes, so just hang in
there!
The image view will update in Interface Builder to show the image resource that you’ve chosen.
You Said You’d Tell Us About Loading Hi-Res Images for the iPhone 4. How Do We Do It?
That’s the best part!
There’s really nothing to do that you don’t already know. To accommodate
the higher scaling factor of the iPhone 4, you just create image
resources that are two times the horizontal and vertical resolution, and
then name them with the same filename as your original low-res images,
but with the suffix @2x (for example, Image.png becomes Image@2x.png).
Finally, add them to your project resources like any other resource.
Within your projects,
just reference the low-res image, and the hi-res image is loaded
automatically on the correct devices, as needed!
Connecting to the Outlet
To display an animation, we need to access the object from the ImageHop view controller. Let’s connect the image view to the imageView outlet that we created earlier.
Within the Document window,
Control-drag from the File’s Owner icon to the image view icon in the
Document window or to the graphical representation in the view window.
When prompted for the outlet, choose imageView, as shown in Figure 4.
Now that the image view has been added, let’s look at the code we need to add to change from a static image to an animation.
Animating the Image View
To truly customize an image view, we need to write some code. Animating images requires us to build an array of image objects (UIImage) and pass them to the image view. Where should we do this? As with the last project, the ViewDidLoad method of our view controller provides a convenient location for doing additional setup for the view, so that’s what we’ll use.
Switch back into Xcode, and open the view controller implementation file, ImageHopViewController.m. Find the ViewDidLoad
method and uncomment it, and then add the following code to the method.
Note that we’ve removed lines 7–20 to save space (they follow the same
pattern as lines 4–6 and 21–23), as shown in Listing 2.
Listing2.
1: - (void)viewDidLoad { 2: NSArray *hopAnimation; 3: hopAnimation=[[NSArray alloc] initWithObjects: 4: [UIImage imageNamed:@"frame-1.png"], 5: [UIImage imageNamed:@"frame-2.png"], 6: [UIImage imageNamed:@"frame-3.png"], ... 21: [UIImage imageNamed:@"frame-18.png"], 22: [UIImage imageNamed:@"frame-19.png"], 23: [UIImage imageNamed:@"frame-20.png"], 24: nil 25: ]; 26: imageView.animationImages=hopAnimation; 27: imageView.animationDuration=1; 28: [hopAnimation release]; 29: [super viewDidLoad]; 30: }
|
To configure the image view for animation, first an array (NSArray) variable is declared (line 2) called hopAnimation. Next, in line 3, the array is allocated and initialized via the NSArray instance method initWithObjects. This method takes a comma-separated list of objects, ending with nil, and returns an array.
The image objects (UIImage)
are initialized and added to the array in lines 4–24. Remember that
you’ll need to fill in lines 7–20 on your own; otherwise, several frames
will be missing from the animation!
Once an array is populated with image objects, it can be used to set up the animation of an image view. To do this, set the animationImages property of the image view (imageView) to the array. Line 6 accomplishes this for our example project.
Another image view property that we’ll want to set right away is the animationDuration. This is the number of seconds it takes for a single cycle of the animation to be played. If the duration is not
set, the playback rate will be 30 frames per second. To start, our
animation will be set to play all the frames in 1 second, so line 27
sets the imageView.animationDuration to 1.
Finally, in line 28, we’re finished with the hopAnimation array, so it can be released.
Starting and Stopping the Animation
A little later in this
tutorial, we’ll be adding controls to change the animation speed and to
start/stop the animation loop. You’ve just learned how the animationDuration property can change the animation speed, but we’ll need three more properties/methods to accomplish everything we want:
isAnimating: This property returns true if the image view is currently animating its contents.
startAnimating: Starts the animation.
stopAnimating: Stops the animation if it is running.
If you run the application now,
it will work, but only a static image will display. The image view does
not start animating until the startAnimating method is called. We’ll take care of that when implementing the view controller logic.
Adding a Slider
The next piece that our
interface needs is the slider that will control the speed. Return to
Interface Builder and the view, and then navigate to the Objects Library
and drag the slider (UISlider) into
the view, just under the image view. Using the resize handles on the
slider, click and drag to size it to about two-thirds of the image view
width and align it with the right side of the image view. This leaves
just enough room for a label to the left of the slider.
Because a slider has no visual
indication of its purpose, it’s a good idea to always label sliders so
that your users will understand what they do. Drag a label object (UILabel) from the Library into your view. Double-click the text and set it to read Speed:. Position it so that it is aligned with the slider, as shown in Figure 5.
Setting the Slider Range Attributes
Sliders make their current settings available through a value property that we’ll be accessing in the view controller. To change the range of values that can be returned, we
need to edit the slider attributes. Click to select the slider in the
view, and then open the Attributes Inspector (Command+1), as shown in Figure 6.
The Minimum, Maximum, and
Initial fields should be changed to contain the smallest, largest, and
starting values for the slider. For this project, use .25, 1.75, and
1.0, respectively.
Where Did These Min, Max, and Initial Values Come From?
This is a great question, and
one that doesn’t have a clearly defined answer. In this application, the
slider represents the speed of the animation, which, as we’ve
discussed, is set through the animationDuration
property of the image view as the number of seconds it takes to show a
full cycle of an animation. Unfortunately, this means the faster animations would use smaller numbers and slower
animations use larger numbers, which is the exact opposite of
traditional user interfaces where “slow” is on the left and “fast” is on
the right. Because of this, we need to reverse the scale. In other
words, we want the big number (1.75) to appear when the slider is on the
left side and the small number (.25) on the right.
To reverse the scale, we take
the combined total of the minimum and maximum (1.75 + 0.25), and
subtract the value returned by the slider from that total. For example,
when the slider returns 1.75 at the top of the scale, we’ll calculate a
duration of 2 – 1.75, or 0.25. At the bottom of the scale, the
calculation will be 2 – 0.25, or 1.75.
Our initial value will be 1.0, which falls directly in the middle of the scale.
Make
sure the Continuous check box isn’t checked. This option, when enabled,
will have the control to generate a series of events as the user drags
back and forth on the slider. When it isn’t enabled, events are
generated only when the user lifts his or her finger from the screen.
For our application, this makes the most sense and is certainly the
least resource-intensive option.
The slider can also be
configured with images at the minimum and maximum sliders of the
control. Use the Min Image and Max Image drop-downs to select a project
image resource if you’d like to use this feature. (We’re not using it in
this project.)
Connecting to the Outlet
For convenient access to the slider, we created an outlet, animationSpeed,
that we’ll be using in the view controller. To connect the slider to
the outlet, Control-drag from the File’s Owner icon to the slider object
in the view or the slider icon in the Document window. When prompted,
choose the animationSpeed outlet.
By the Way
In case you’re
wondering, it’s certainly possible to implement this application without
an outlet for the slider. When the slider triggers an action, we could
use the sender variable to reference the slider value
property. That said, this approach will allow us to access the slider
properties anywhere in the view controller, not just when the slider
triggers an action.
Connecting to the Action
When a user drags the slider and releases his finger, the application should trigger the action method setSpeed. Create this connection by selecting the slider and then opening the Connections Inspector (Command+2).
Drag from the circle
beside Value Changed to the File’s Owner icon in the Document window.
When prompted, choose to connect to the setSpeed action. Once complete, Connections Inspector should reflect this change and show both the setSpeed and animationSpeed connections, as demonstrated in Figure 7.
That completes the major parts of the UI, but there’s still some cleanup work to do.