iOS Recipes - Matt Drance [25]
InfiniteImages/PRPTiledLayer.m
+ (CFTimeInterval)fadeDuration {
return 0.00;
}
The final effect is quite satisfying, with the album art wrapping in all directions without any impact on the responsiveness of scrolling. Rapid scrolling causes images to lag behind a little, a side effect of using the tiled layer, but in general performance is quite acceptable, even on Retina Display devices.
Recipe 12 Play Tracks from a Wall of Album Art
Problem
You’ve used scroll views and tile layers to create a colorful wall of album art that wraps in all directions. Now you want to select just one of the albums and play some of the music from it. What do you need to do to extend Recipe 11, Scroll an Infinite Wall of Album Art ?
Solution
In the previous recipe, we explored scroll views and tile layers but mostly used the album art from the iPod library to provide an attractive visual background. You can do a lot more with the iPod library than just grab art; you can build a playlist of songs, play and control songs, and access the rich supply of metadata (see Figure 16, iPod playback control ).
Figure 16. iPod playback control
* * *
Our first task is to modify the previous code so that we can detect which album has been selected. The simplest way to do that is to use a UITapGestureRecognizer. We can add this in the MainViewController and then call the new tapDetected method using the initWithTarget:action: method. We could add the recognizer to several views, but by adding it to the tiles view, the touch location that is returned to us is based on the coordinate system for that view, which makes it much easier for us to work out which album was selected.
We will put most of the iPod library code in a new controller class, PRPMusicViewController, so we create an instance of that, musicController, and set up its frame as centered on the current view.
InfinitePlayback/MainViewController.m
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(tapDetected:)];
[tiles addGestureRecognizer:tap];
[tap release];
musicController = [[PRPMusicViewController alloc]
initWithNibName:@"PRPMusicControllerView" bundle:nil];
CGFloat xPos = (width-musicController.view.frame.size.width)/2;
CGFloat yPos = (height-musicController.view.frame.size.height)/2;
musicController.view.frame = CGRectOffset(musicController.view.frame, xPos, yPos);
We mainly use the tapDetected method as a toggle to show or hide the music controller. In this simple example, when the music controller is hidden, we also stop the music. We could let the music play on, but because the controller has already been dismissed, there is no way to control the music that is currently playing. Before we can present the music controller, we need to work out which album was selected. We call the collectionFromTouch method in the PRPTileView class to convert the touch point into an MPMediaItemCollection item. We can then set the mCollection property to a new playlist of all the tracks in this album.
Because we are adding our musicController view as a subview of the main view, the viewWillAppear: method will not be activated after it is loaded, so we need to call it manually here to complete our playback initialization.
InfinitePlayback/MainViewController.m
- (void)tapDetected:(UITapGestureRecognizer *)tap {
PRPTileView *tiles = (PRPTileView *)tap.view;
if (showingAlbum) {
[musicController ZoomOutView];
[musicController.myPlayer stop];
showingAlbum = NO;
} else {
CGPoint tapPoint = [tap locationInView:tiles];
MPMediaItemCollection *mCollection = [tiles
collectionFromTouch:tapPoint];
musicController.mCollection = [MPMediaItemCollection
collectionWithItems:[mCollection items]];
[musicController viewWillAppear:NO];
[self.view addSubview:musicController.view];
[musicController ZoomInView];
showingAlbum = YES;
}
}
The collectionFromTouch method takes the touchPoint we detected, works out the position in the CATiledLayer, and from there calculates the index into the array of albumCollections. We can