13. Enhanced Slideshow App

Serialization Data with NSCoder and Playing Video

image

13.1 Introduction

The Enhanced Slideshow app adds video capabilities to Chapter 12’s SlideShow app. When modifying a slideshow, the user touches the “Add Picture/VideoButton to view the iPhone’s photo library, which now includes videos (Fig. 13.1). Selecting a video’s thumbnail displays a close-up of that thumbnail (Fig. 13.2). The user touches the play Button to play the video without adding it to the slideshow. Touching the “ChooseButton adds the selected video to the slideshow. A new effect is displayed when the user touches the “Set EffectButton (Fig. 13.3). The Flip effect rotates the previous image horizontally to reveal the next image on its reverse (Fig. 13.4). All user-created slideshows are saved and available each time the app is run.

Fig. 13.1 | iPhone photo library.

image

Fig. 13.2 | Viewing a video.

image

Fig. 13.3 | Setting the image transition effect.

image

Fig. 13.4 | Flip effect—rotates slide horizontally revealing the next slide underneath.

image

13.2 Test-Driving the Enhanced Slideshow App

Opening the completed application

Open the Enhanced Slideshow app project’s directory and double click EnhancedSlideshow.xcodeproj to open it in Xcode. Apps running in the iPhone simulator cannot access the iPod music library, so if you’re using the simulator you cannot add slideshow background music. Also, you cannot add video to the photo libraries of iPhones older than the 3GS (nor to the simulator’s photo library), so slideshows running on those devices are limited to images. This test-drive assumes you’re running the app on an iPhone 3GS.

Creating a New Slideshow

Touch the “NewButton in the top-right corner of the app and enter My Slideshow1 in the “Name this slideshowText Field. Touch the “Add Picture/VideoButton to view the iPhone’s photo library. This library stores pictures and videos taken by the iPhone’s camera. Touch a video’s thumbnail to see a close-up of that thumbnail. Press the play Button to watch the video. Add the video to the slideshow by touching the “ChooseButton. Enter My Video1 in the “Add a title for the videoText Field. Your video is displayed in the “Edit Slideshow” screen, represented by a thumbnail and the title you specified. Add two more pictures or videos, then select background music for this slideshow. Touch the “Set Effect” Button and choose Flip. Touch the “BackButton to return to the list of slideshows.

Playing a Slideshow

Touch the “PlayButton next to your slideshow. Your videos and images are displayed. Images remain on the screen for five seconds while the background music plays. Videos remain on the screen for their entire duration. Each video silences the background music in favor of the video’s audio. Exit the app and touch its icon to run the Enhanced Slideshow again. Unlike the original Slideshow app, notice that the slideshows are persistent.

13.3 Overview of the Technologies

The Enhanced Slideshow app plays videos from the iPhone’s photo library using an MPMoviePlayerController. The user chooses videos for the slideshow using a UIImagePickerController. We specify that all available media types in the photo library (images and videos) are available to the user by setting the UIImagePickerController’s mediaTypes property.

We added another Core Animation image transition to this version of the app. The UIViewAnimationTransitionFlipFromRight animation transition flips a slideshow image horizontally to reveal the next image.

In the Enhanced Slideshow app, slideshows are stored on the iPhone for viewing later. We’ve saved textual data previously, but saving objects (such as those representing images and videos) requires a technique called object serialization. A so-called serialized object is an object represented as a sequence of bytes that includes the object’s data as well as information about the object’s type and the types of data stored in the object. We serialize an object (also referred to as encoding in Objective-C) using a subclass of the NSCoder abstract class. This class acts as an interface for storing Objective-C objects on disk (referred to as archiving). Class NSKeyedArchiver is a concrete subclass of NSCoder used to save the entire slideshow list’s object graph to a file. An object graph is a representation of an object, all of the objects that it references, all of the objects that those objects reference, and so on. The NSKeyedArchiver handles creation of the object graph and serializing it.

After a serialized object has been written to a file, it can be read from the file and deserialized—that is, the type information and bytes that represent the object and its data can be used to recreate the object graph in memory. This is also referred to as decoding or unarchiving and is accomplished with a subclass of NSCoder.

13.4 Building the App

Only classes and methods that have changed from Chapter 12’s Slideshow app are shown here. The complete source code is located in the project’s directory. For any class or method that was previously discussed in Chapter 12 and that is shown in this chapter, we highlight the new or changed lines. For new classes and methods, we highlight new and important lines, as per our usual convention.

13.4.1 Class MediaItem

The new class MediaItem (Fig. 13.5) represents an image or video in a slideshow. Lines 6–10 declare the _mediaType enum. The typedef allows us to refer to this enum as MediaType. The MediaTypeImage enum constant indicates that a MediaItem represents a picture and the MediaTypeVideo constant indicates that a MediaItem represent a video.

Fig. 13.5 | MediaItem class represents an image or a video.

image

MediaItem implements the NSCoding protocol (line 12), which declares two methods to encode and decode objects of the implementing class. This allows us to store MediaItems on the iPhone. The initWithCoder: method creates a MediaItem using an NSCoder object. The encodeWithCoder: method encodes a MediaItem, also using an NSCoder.

Class MediaItem declares three instance variables (lines 14–16). The MediaType variable (line 14) type indicates if this MediaItem represents an image or a video. The NSString description (line 15) stores a brief title of this MediaItem if it’s a video. Images do not store anything in description. The variable data of type id (line 16) represents the file path of this MediaItem’s image or video. Lines 20–22 declare each of MediaItem’s instance variables as properties. The initWithType:description:data: method (lines 25–26) receives arguments corresponding to each of MediaItem’s instance variables and creates a new MediaItem.

MediaItem Class Definition

The initWithType:description:data: method (Fig. 13.6, lines 12–24) creates a new MediaType. Lines 18–20 set each of MediaItem’s instance variables to their respective arguments.

Fig. 13.6 | MediaItem class implementation.

image

image

The initWithCoder: method (lines 27–43) decodes each of MediaItem’s instance variables from the given NSCoder. This method is called to deserialize a MediaItem. Line 33 decodes a value for the MediaType using NSCoder’s decodeIntForKey: method. We supply "type" as the key for this decoding since we use this same key when encoding this instance variable. Lines 36 and 39 decode values for MediaItem’s remaining instance variables using NSCoder’s decodeObjectForKey: method.

The encodeWithCoder: method (lines 46–53) serializes the MediaItem by encoding each of MediaItem’s instance variables using the given NSCoder. Line 48 passes the key "type" to NSCoder’s encodeIntForKey: method. The key identifies the encoding and is required for decoding. Lines 51–52 encode MediaItem’s remaining instance variables using NSCoder’s encodeObjectForKey: method.

MediaItemCreator Interface Declaration

The new class MediaItemCreator (Fig. 13.7) controls a view that allows the user to title a selected video. Lines 10–11 declare MediaItemCreator’s delegate and the Text Field used to enter a video name. The NSURL variable media stores the location of the video that is being titled. Lines 16–17 declare delegate and media as properties. Method finishedNaming: (line 20) passes the video’s name to the delegate when the user touches the “DoneButton on the keyboard. Lines 27–28 declare the NameChooserDelegate. Method mediaItemCreator:didCreateMediaItem: is called to pass the new video MediaItem to the delegate.

Fig. 13.7 | Presents the user with an interface for creating a MediaItem.

image

To build this view’s GUI, open MediaItemCreator.xib in Interface builder then add a Label and set its text to Add a label for the video. Add a Text Field below the Label then open the Inspector window and connect the finishedNaming: method to the Text Field’s Did End On Exit event in the Inspector window.

MeidaItemCreator Class Definition

Lines 7–8 of class MediaItemCreator (Fig. 13.8) synthesize properties delegate and media. This viewDidLoad method (lines 11–15) sets up MediaItemCreator’s view. We call the superclass’s viewDidLoad method (line 13) then call UITextField’s becomeFirstResponder method to select the “Add a label for the videoText Field (line 14). This displays the keyboard. The finishedNaming: method (lines 18–26) creates a new MediaItem that is labeled using the textField’s text property (lines 21–22) and passes the MediaItem to the delegate’s mediaItemCreator:didCreateMediaItem: method (line 25). This passes to the SlideshowDataViewController a MediaItem representing the selected video with the user-entered title.

Fig. 13.8 | MediaItemCreator class implementation.

image

13.4.2 Class Slideshow

The Slideshow class (Fig. 13.9) represents a user-created slideshow. In the original Slideshow app, we did not create a separate class to represent slideshows. The increased complexity of slideshows in the Enhanced Slideshow app and the requirements for archiving slideshows make it important to follow the Model-View-Controller design pattern. So we create a new class representing a slideshow. The Enhanced Slideshow app adds an enumeration constant to the TransitionEffect enum (lines 8–13). TransitionEffectFlip (line 12) represents an image transition which flips the previous image horizontally to reveal the next image on its the backside.

Fig. 13.9 | Interface for class Slideshow which represents a slideshow.

image

The NSMutableArray variable media stores the images and videos in this Slideshow (line 18). Line 19 declares an MPMediaItemCollection that stores the background music for a Slideshow. We use a TransitionEffect (line 20) to represent the type of animated transition between this Slideshow’s slides. An NSString stores this Slideshow’s title. Lines 25–28 declare Slideshow’s instance variables as properties. Slideshow’s firstImage method (lines 30) returns the Slideshow’s first image, which will not be the first slide if the Slideshow starts with a video.

Lines 34–37 declare a category named NSCoding which adds the two NSCoding protocol methods to UIImage (lines 35–36). The initWithCoder: method uses an NSCoder to deserialize a UIImage and the encodeWithCoder: method uses an NSCoder to serialize a UIImage.

Methods init and initWithCoder: of Class Slideshow

The init method (Fig. 13.10, lines 12–20) calls the superclass’s init method (line 16) then initializes the media NSMutableArray (line 17). The initWithCoder: method (lines 23–42) receives an NSCoder that is used to deserialize a Slideshow. Lines 29–38 use NSCoder’s decodeObjectForKey method to deserialize each Slideshow instance variable.

Fig. 13.10 | Methods init and initWithCoder: of class Slideshow.

image

Methods encodeWithCoder: and firstImage of Class Slideshow

The encodeWithCoder: method (Fig. 13.11, lines 45–51) uses NSCoder’s encodeObject:forKey: method to serialize Slideshow’s media, music and title instance variables. We can encode the media because we made MediaItem implement the NSCoding protocol earlier in the chapter. We use NSCoder’s encodeInt:forKey: method to serialize the TransitionEffect enum value because an enum is equivallent to an int.

Fig. 13.11 | Methods encodeWithCoder: and firstImage of class Slideshow.

image

The firstImage method (lines 54–73) returns the first image slide in this Slideshow. Lines 60–70 loop through each MediaItem and check if its type property is MediaTypeImage (line 65). If it is, this MediaItem represents a picture and is assigned to firstImage (line 67). Line 68 sets found to YES indicating that the first image has been found.

NSCoding category For UIImage

Class UIImage does not implement the NSCoding protocol, so we use the category feature of Objective-C to add the required serialization methods to UIImage. Method initWithCoder: (Fig. 13.12, lines 89–100) initializes a UIImage using an NSCoder. NSCoder’s decodeObjectForKey: method returns an NSData object representing the UIImage (line 94). We pass this to UIImage’s initWithData: method to initialize the UIImage from the saved data (line 95).

Fig. 13.12 | NSCoding category For UIImage.

image

The encodeWithCoder: method (lines 103–110) gets an NSData object representing this UIImage using the UIImagePNGRepresentation function (line 105). We then serialize this object by passing the NSData object to NSCoder’s encodeObject:forKey: method (line 108).

13.4.3 Class RootViewController

This section shows only the updated portions of the RootViewController class from Section 12.4.1.

Method viewDidLoad of class RootViewController

RootViewController’s viewDidLoad method (Fig. 13.13) loads the list of slideshows from the app’s data directory on the iPhone each time RootViewController’s view is loaded. Lines 15–16 call the NSSearchPathForDirectoriesInDomains function to get an NSArray containing one element—the path for this app’s documents directory. We retrieve this directory path (line 19), then append the name of the slideshow data file (data.slideshow) to the app’s data directory.

Fig. 13.13 | Method viewDidLoad of class RootViewController.

image

image

Lines 26–27 get an NSMutableArray containing all of the slideshows by deserializing the data.slideshows file. NSKeyedUnarchiver’s unarchiveObjectWithFile: method returns an object graph of type id, representing the NSArray of saved slideshows. NSKeyedUnarchiver is a concrete subclass of NSCoder that deserializes an object. NSObject’s mutableCopy method converts the NSArray to an NSMutableArray. If there are no saved slideshows, we initialize slideshows as a new NSMutableArray (lines 31–32). Lines 34–55 set up the app’s navigation bar the same way as in the original Slideshow app.

Method nameViewController:didGetName: of class RootViewController

The nameViewController:didGetName: method (Fig. 13.14) is called when the user finishes entering a name for a new slideshow. UIViewController’s dismissModalViewControllerAnimated: method hides the NameViewController’s view (line 85). Lines 88–89 create a new Slideshow and set its title property to the given NSString. We then create a new SlideshowDataViewController using the initWithSlideshow: method (lines 92–93). We call NSMutableArray’s addObject: method to add the new Slideshow to the list of Slideshows.

Fig. 13.14 | Method nameViewController:didGetName: of class RootViewController.

image

Method slideshowCellDidSelectEditButton: of class RootViewController

Method slideshowCellDidSelectEditButton: (Fig. 13.15) displays the view for editing the selected Slideshow. Line 107 calls method UITableView’s indexPathForCell: to get an NSIndexPath representing the index of the touched UITableViewCell. We create a new SlideshowDataViewController using its initWithSlideshow method (lines 110–112). Line 112 gets the selected Slideshow by passing the NSIndexPath’s row property to NSMutableArray method objectAtIndex:. Lines 115–116 call UINavigationController’s pushViewController:animated: method to display the Slideshow-editing view.

Fig. 13.15 | Method slideshowCellDidSelectEditButton: of class RootViewController.

image

Method tableView:cellForRowAtIndexPath: of Class RootViewController

Method tableView:cellForRowAtIndexPath: (Fig. 13.16) retrieves the UITableViewCell specified by the given NSIndexPath. Lines 153–165 attempt to reuse a cell from the given tableView using UITableView’s dequeReusableCellWithIdentifier: method. We set the new SlideshowCell’s delegate to this RootViewController (line 167). Line 170 sets the UITableViewCell’s selectionStyle property to UITableViewCellSelectionStyleNone so that no action is taken when the UITableViewCell is touched. Line 173 gets the SlideShow corresponding to the touched UITableViewCell. Line 176 calls the Slideshow’s firstImage method to get a UIImage representing the first picture in the selected Slideshow. Lines 177–178 set the SlideshowCell’s thumbnail to that UIImage and title to the Slideshow’s title.

Fig. 13.16 | Method tableView:cellForRowAtIndexPath: of class RootViewController.

image

13.4.4 Class SlideshowDataViewController

This section shows only the updated portions of the SlideshowDataViewController class from Section 12.4.4.

Method initWithSlideshow: of Class SlideshowDataViewController

The Enhanced Slideshow app’s SlideshowDataViewController class (Fig. 13.17) has one additional instance variable not in the original Slideshow app. The slideshow currently being edited is stored as a pointer to an object of the new Slideshow class. Line 13 sets this slideshow instance variable to the given Slideshow and calls its retain method—increasing its retain count by one and ensuring that the Slideshow’s memory is not freed until the SlideshowViewController no longer needs it.

Fig. 13.17 | Method initWithSlideshow: of class SlideshowDataViewController.

image

Method addPhoto of Class SlideshowDataViewController

The addPhoto method (Fig. 13.18) allows the user to choose an image or video from the photo library and add it to the slideshow when the user touches the “Add Picture/VideoBar Button Item. First, we initialize the UIImagePickerController if it hasn’t been initialized yet (lines 137–140). We set the allowsImageEditing property to YES (line 141) to allow the user to edit the image or video before it’s added to the slideshow. For example, the user can zoom into a particular part of an image with a pinch gesture, or the user can trim a video by dragging the handles at the left or right side of the video’s thumbnail strip at the top of the screen. We set the sourceType to UIImagePickerControllerSourceTypePhotoLibrary to specify that the image or video is to be picked from the user’s photo library. Lines 148–150 call UIImagePickerController’s availableMediaTypesForSourceType method, passing as an argument the source type representing the iPhone’s photo library (UIImagePickerControllerSourceTypePhotoLibrary). We assign the result to imagePicker’s mediaTypes property to specify that we want the imagePicker to display both images and videos. We then show the imagePicker (line 157).

Fig. 13.18 | Method addPhoto of class SlideshowDataViewController.

image

Method imagePickerController:didFinishPickingMediaWithInfo: of Class SlideshowDataViewController

The imagePickerController:didFinishPickingMediaWithInfo: method (Fig. 13.19) is called when the user adds an image or video from the image picker to the slideshow. Line 165 hides the image picker by calling UIViewController’s dismissModalViewControllerAnimated: method. The info NSDictionary contains information on the chosen image or video. We use NSDictionary’s valueForKey: method to get info’s value for key UIImagePickerControllerMediaType (line 168). If this value equals kuTypeImage, we know the user touched an image. Lines 172–173 get info’s UIImage for key UIImagePickerControllerEditedImage. This is the user’s chosen picture. We then create a new MediaItem using the UIImage and add the MediaItem to the Slideshow (lines 176–180). Line 183 gets SlideshowDataViewController’s UITableView. We then insert the new MediaItem into the UITableView of slides (lines 186–188).

Fig. 13.19 | Method imagePickerController:didFinishPickingMediaWithInfo: of class SlideshowDataViewController.

image

image

If the user did not touch an image they must have touched a video. Line 193 gets info’s NSURL for the key UIImagePickerControllerMediaURL. This represents the chosen video’s location on the iPhone. We use NSURL’s absoluteString method to get a NSString representing the URL then call NSString’s lastPathComponent to extract the video’s name from the end of its file path. The NSSearchPathForDirectoriesInDomains function returns an NSString representing the directory path for this app’s documents directory (lines 199–200). The video file was automatically saved in a temporary directory. We need to move the video out of this directory, so that it’s not deleted when the app closes. NSString’s stringByAppendingPathComponent: method is used to append the video’s name to the end of the documents directory path to get the video’s current file path (line 202). Line 209 gets the default NSFileManager. NSFileManager's copyItemAtPath:newPath:error: method is used to move the video file from the temporary directory to the app’s data directory. We get the path for the video file in the temporary directory is saved using NSURL’s path method (line 212).

Lines 215–217 create a new MediaItemCreator and set its delegate to self. We use NSURL’s fileURLWithPath: method to convert the video file path in the app’s data directory to an NSURL (line 220). Line 226 calls UIViewController’s presentModalViewController:animated: method to display MediaItemCreator’s view.

Method mediaItemCreator:didCreateMediaItem: of Class SlideshowDataViewController

The mediaItemCreator:didCreateMediaItem: method (Fig. 13.20) is called when the user finishes naming a video MediaItem using MediaItemCreator’s view. Line 232 adds the MediaItem to the current Slideshow. Lines 238–243 add a new row displaying the new MediaItem to SlideshowDataViewController’s UITableView and hides MediaItemCreator’s view.

Fig. 13.20 | Method mediaItemCreator:didCreateMediaItem: of class SlideshowDataViewController.

image

Methods mediaPicker:didPickMediaItems: and addEffect of Class SlideshowDataViewController

The mediaPicker:addPickMediaItems: method (Fig. 13.21, lines 263–271) is called when the user touches the “DoneButton after selecting songs from the iPod music library. Line 267 assigns the MPMediaItemCollection containing the selected songs to the Slideshow’s music property. UIViewController’s dismissModalViewControllerAnimated: removes the media picker from view (line 270). The addEffect method (lines 274–286) displays a UIActionSheet when the user touches the “Add EffectButton. Lines 280–282 create the UIActionSheet that displays the transition options. We add the Flip effect as a third argument to otherButtonTitles:.

Fig. 13.21 | Methods mediaPicker:didPickMediaItems: and addEffect of class SlideshowDataViewController.

image

Methods startSlideshow and actionSheet:clickedButtonAtIndex: of Class SlideshowDataViewController

Method startSlideshow (Fig. 13.22, lines 289–303) begins the slideshow. First, we create the SlideshowViewController (lines 291–292) then set its slideshow property to the current Slideshow (line 295). We hide the navigation bar (line 298) then show the SlideshowViewController using UINavigationController’s pushViewController:animated: method (line 301). Method actionSheet:clickedButtonAtIndex: (lines 306–310) sets this Slideshow’s effect property to the given NSInteger representing the effect the user selected. This saves the user’s choice of image transition effect.

Fig. 13.22 | Methods startSlideshow and actionSheet:clickedButtonAtIndex: of class SlideshowDataViewController.

image

Method tableView:cellForRowAtIndexPath: of Class SlideshowDataViewController

Method tableView:cellForRowAtIndexPath: (Fig. 13.23) retrieves the UITableViewCell at the given NSIndexPath. Lines 328–339 attempt to reuse a cell from the tableView. We empty any subviews from the UITableViewCell using UIView’s removeFromSuperView method (lines 342–343). Line 346 gets the MediaItem corresponding to the touched UITableViewCell. If this MediaItem’s type is MediaTypeImage, the MediaItem represents an image so we access its data property to receive a UIImage (349–352). Line 355 creates a new UIImageView using the retrieved UIImage, then resizes it to fit the cell (lines 358–367). We then add the configured UIImageView to the cell (line 368). If the MediaItem represents a video, we need to title the UITableCell with the video’s title. Lines 376–378 create a new UILabel and set its text to “Video:” followed by the MediaItem’s description. Lines 379–383 size and position the Label then add it to the UITableViewCell.

Fig. 13.23 | Method tableView:cellForRowAtIndexPath: of class SlideshowDataViewController.

image

image

Method tableView:moveRowAtIndexPath:toIndexPath: of Class SlideshowDataViewController

When the user reorders slideshow items, method tableView:moveRowAtIndexPath:toIndexPath: (Fig. 13.24) moves a MediaItem from the UITableViewCell specified by fromIndexPath to the UITableViewCell specified by toIndexPath. We use NSMutableArray’s objectAtIndex: and insertObject:atIndex: methods to reorder the Slideshow’s media array according to the NSIndexPaths.

Fig. 13.24 | Method tableView:moveRowAtIndexPath:toIndexPath: of class SlideshowDataViewController.

image

13.4.5 Class EnhancedSlideshowAppDelegate

An app delegate (subclass of UIApplicationDelegate) responds to messages from the app’s singleton UIApplication object after the app loads and just before it terminates. Xcode generates app delegate classes for each project which can be overwritten to add behavior at load or launch.

Method applicationWillTerminate: of Class EnhancedSlideshowAppDelegate

The EnhancedSlideshowAppDelegate class was automatically created by Xcode when we created the Enhanced Slideshow app project. We ignored this file in the previous Slideshow app; however, to save the slideshows when the user exits the app, we must add code to the app delegate’s applicationWillTerminate: method (Fig. 13.25). Lines 34–38 get this app’s data directory. We add data.slideshows to the end of this directory path. Lines 45–46 access the UINavigationController’s viewController property to get the app’s RootViewController. We pass the RootViewController’s NSMutableArray of Slideshows to NSKeyedArchiver’s archiveRootObject:toFile: method. This serializes the slideshow list’s object graph (i.e., the slideshows and their contents) so it can be loaded the next time the app executes.

Fig. 13.25 | Method applicationWillTerminate: of class EnhancedSlideshowAppDelegate.

image

13.4.6 Class SlideshowViewController

The SlideshowViewController class (Fig. 13.26) controls a view that plays the Slideshow stored in the slideshow instance variable (line 10). An MPMoviePlayerController is used to play full screen movies (lines 12). Video on the iPhone plays only in landscape orientation, so the video will not rotate in response to reorienting the iPhone. Line 18 declares Slideshow as a property.

Fig. 13.26 | Controller for a view that shows a slideshow.

image

SlideshowViewController declares seven methods (lines 21–34):

nextImageViewWithMedia:—returns a UIImage representing a given MediaItem’s image. This method is called only for MediaItem’s representing images.

displayNewImage:—displays a MediaItem representing the next image in the slideshow

displayNewVideo:—displays a MediaItem representing the next video in the slideshow

videoFinished:—moves to the next slide in the slideshow when a video finishes playing

exitShow:—stops the music and returns to the previous view.

changeSlide:—moves to the next slide in the slideshow or calls the exitShow method if there are no more slides

transitionFinished:finished:context:—called when an image transition animation completes

Method nextImageViewWithMedia: of Class SlideshowViewController

Method nextImageViewWithMedia (Fig. 13.27) returns a UIImageView representing the given MediaItem. We retrieve the UIImage by accessing the MediaItem’s data property (line 22). Line 25 creates a new UIImageView using the retrieved UIImage. Line 27 accesses SlideshowViewController’s UIView’s bounds property to get a CGRect representing the screen’s bounds. We pass this CGRect to imageView’s expandToFill: method to resize our UIImageView to fill the entire screen (line 30). This expands the image as much as possible in the current orientation without distorting it. Lines 31–36 get the expanded UIImageView’s frame and center the UIImageView in the screen. Lines 40–43 specify that SlideshowViewController’s UIImageView remains centered as the iPhone rotates. We set the UIImageView’s autoresizingMask property by combining all desired options using the bitwise OR operator (|).

Fig. 13.27 | Method nextImageViewWithMedia: of class SlideshowViewController.

image

Method changeSlide of Class SlideshowViewController

Method changeSlide method (Fig. 13.28) advances the Slideshow by displaying its next image or video. If pictureIndex equals the number of slides in this Slideshow (slideshow.media.count), we call the exitShow method to end the Slideshow (lines 64–65). Otherwise, we get this Slideshow’s next MediaItem (line 69). If the MediaItem’s type property is MediaTypeImage, we pass the MediaType to the displayNewImage: method to display that picture as the next slide (lines 72–73). Otherwise, the MediaItem represents a video so we pass it to the displayNewVideo: method (lines 74–75).

Fig. 13.28 | Method changeSlide of class SlideshowViewController.

image

Method displayNewImage: of Class SlideshowViewController

Method displayNewImage: (Fig. 13.29) displays an image (represented by the given MediaItem) as the next slide in this Slideshow. Lines 89–137 implement the Fade and Slide transitions the same way as in the original Slideshow app.

Fig. 13.29 | Method displayNewImage: of class SlideshowViewController.

image

image

If this Slideshow uses the Flip image transition (line 138), we start a new Core Animation block describing an animation with a duration of two seconds which calls the transitionFinished:finished:context: method upon completion (lines 146–147). We call UIView’s setAnimationTransition:forView:cache: method, supplying UIViewAnimationTransitionFlipFromRight as the animation transition (lines 150–152). Lines 154–156 remove the previous UIImageView, add the new UIImageView and begin the animation. Lines 161–162 use the performSelector:withObject:afterDelay: method to call the changeSlide method after a five-second delay.

Methods displayNewVideo: and videoFinished of Class SlideshowViewController

Method displayNewVideo: (lines 166–178) displays a video as the next slide in the slideshow. First, we add self as an observer of the MPMoviePlayerPlaybackDidFinishNotification (lines 169–171). Class NSNotificationCenter manages notifications and observers. Notifications are objects that represent an event—in this example the event occurs when a movie’s playback finishes. An observer is an object that should be notified when the event occurs. We access the defaultCenter singleton object, which is where system notifications are posted. Lines 169–171 indicate that the videoFinished method should be called when a MPMoviePlayer finishes playback. We then initialize moviePlayer with the URL of the movie to be played (lines 175–176) and play the movie (line 177).

Fig. 13.30 | Method displayNewVideo: of class SlideshowViewController.

image

The videoFinished method is called when the movie stops playing. We remove self as an observer from the NSNotificationCenter using the removeObserver: method. This stops the object from receiving notifications about movies ending. We then hide the status bar (line 187) and change to the next slide (line 188).

13.5 Suggested Enhancements

The Enhanced Slideshow app provides numerous capabilities not found in its predecessor but there are still features which can improve the app. Currently, each image displays for five seconds before moving to the next slide. Providing a means for the user to adjust this time would allow the slideshow to be more customized. Additionally, the app could enable the user to navigate through the slideshow while it’s playing—left swipe could advance to the next slide and a right swipe could return to the previous slide.

13.6 Wrap-Up

The Enhanced Slideshow app played videos from the iPhone’s photo library using an MPMoviePlayerController. We used a UIImagePickerController to create a graphical interface for the user to chose images and videos. We added a new image transition using Core Animation. The UIViewAnimationTransitionFlipFromRight animation transition enabled images to transition by flipping horizontally. We saved slideshows so they would be accessible between multiple executions of the app using object serialization. We encoded objects using a subclass of NSCoder. An NSKeyedArchiver was used to save the entire slideshow list’s object graph to a file.

In Chapter 14, we build the Voice Recorder app. We use the AVFoundation framework and an AVAudioRecorder object to record the user’s speech through the iPhone’s microphone. We’ll change the app’s AVAudioSession to switch between settings for recording and audio playback. We’ll also use an MFMailComposeViewController to allow the user to send a voice recording as an e-mail attachment directly from the app.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.246.193