Serialization Data with NSCoder
and Playing Video
The Enhanced Slideshow app adds video capabilities to Chapter 12’s SlideShow app. When modifying a slideshow, the user touches the “Add Picture/Video” Button 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 “Choose” Button adds the selected video to the slideshow. A new effect is displayed when the user touches the “Set Effect” Button (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.
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.
Touch the “New” Button in the top-right corner of the app and enter My Slideshow1
in the “Name this slideshow” Text Field. Touch the “Add Picture/Video” Button 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 “Choose” Button. Enter My Video1
in the “Add a title for the video” Text 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 “Back” Button to return to the list of slideshows.
Touch the “Play” Button 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.
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
.
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.
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.
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 MediaItem
s 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 DefinitionThe 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.
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 DeclarationThe 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 “Done” Button on the keyboard. Lines 27–28 declare the NameChooserDelegate
. Method mediaItemCreator:didCreateMediaItem:
is called to pass the new video MediaItem
to the delegate
.
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 DefinitionLines 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 video” Text 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.
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.
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
.
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.
encodeWithCoder:
and firstImage
of Class SlideshowThe 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
.
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).
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).
RootViewController
This section shows only the updated portions of the RootViewController
class from Section 12.4.1.
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.
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.
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 Slideshow
s.
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.
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
.
SlideshowDataViewController
This section shows only the updated portions of the SlideshowDataViewController
class from Section 12.4.4.
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.
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/Video” Bar 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).
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).
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.
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.
mediaPicker:didPickMediaItems:
and addEffect
of Class SlideshowDataViewController
The mediaPicker:addPickMediaItems:
method (Fig. 13.21, lines 263–271) is called when the user touches the “Done” Button 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 Effect” Button. Lines 280–282 create the UIActionSheet
that displays the transition options. We add the Flip
effect as a third argument to otherButtonTitles:
.
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.
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
.
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 NSIndexPath
s.
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.
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.
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.
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
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 (|
).
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).
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.
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.
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).
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).
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.
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.
3.135.246.193