As a courtesy, this is a full free rendering of my book, Programming iOS 6, by Matt Neuburg. Copyright 2013 Matt Neuburg. Please note that this edition is outdated; the current books are iOS 13 Programming Fundamentals with Swift and Programming iOS 13. If my work has been of help to you, please consider purchasing one or both of them, or you can reward me through PayPal at http://www.paypal.me/mattneub. Thank you!
An iOS device can be used for the same purpose as the original iPod — to hold and play music and podcasts. These items constitute the device’s music library; the user can play them with the Music app (formerly called the iPod app on some devices). iOS provides the programmer with various forms of access to the device’s music library; you can:
These abilities are provided by the Media Player framework. You’ll need to link to MediaPlayer.framework and import <MediaPlayer/MediaPlayer.h>
.
Everything in the music library, as seen by your code, is an MPMediaEntity. This is an abstract class that endows its subclasses with the ability to describe themselves through key–value pairs called properties. (This use of the word “properties” has nothing to do with the Objective-C properties discussed in Chapter 12; these properties are more like entries in an NSDictionary.) The repertoire of properties depends on the sort of entity you’re looking at; many of them will be intuitively familiar from your use of iTunes. For example, a media item has a title, an album title, a track number, an artist, a composer, and so on; a playlist has a title, a flag indicating whether it is a “smart” playlist, and so on. The property keys have names like MPMediaItemPropertyTitle
.
To fetch a property’s value, call valueForProperty:
with its key. You can fetch multiple properties with enumerateValuesForProperties:usingBlock:
.
An individual item in the music library is an MPMediaItem, an MPMediaEntity subclass. It has a type, according to the value of its MPMediaItemPropertyMediaType
property: it might, for example, be music, a podcast, an audiobook, or a video. Different types of item have slightly different properties; for example, a podcast, in addition to its normal title, has a podcast title.
An item’s artwork image is an instance of the MPMediaItemArtwork class, from which you are supposed to be able to get the image itself scaled to a specified size by calling imageWithSize:
; my experience is that in reality you’ll receive an image of any old size the system cares to give you, so you may have to scale it further yourself. This, for example, is what my Albumen app does:
MPMediaItemArtwork* art = //... UIImage* im = [art imageWithSize:CGSizeMake(36,36)]; // but it probably *isn't* 36 by 36; scale it so that it is if (im) { CGFloat scalew = 36.0/im.size.width; CGFloat scaleh = 36.0/im.size.height; CGFloat scale = (scalew < scaleh) ? scalew : scaleh; CGSize sz = CGSizeMake(im.size.width*scale, im.size.height*scale); UIGraphicsBeginImageContextWithOptions(sz, NO, 0); [im drawInRect:CGRectMake(0,0,sz.width,sz.height)]; im = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); }
A playlist is an MPMediaPlaylist. As you would expect, it has items
and a count
of those items. It inherits those properties from its superclass, MPMediaItemCollection, which is the other MPMediaEntity subclass. I’ll talk more about MPMediaItemCollection in a moment.
Obtaining actual information from the music library requires a query, an MPMediaQuery. First, you form the query. There are two main ways to do this:
MPMediaQuery provides several class methods that form a query ready to ask the music library for all of its songs, or all of its podcasts, and so on. Here’s the complete list:
songsQuery
podcastsQuery
audiobooksQuery
playlistsQuery
albumsQuery
artistsQuery
composersQuery
genresQuery
compilationsQuery
You can attach to the query one or more MPMediaPropertyPredicate instances, forming a set (NSSet) of predicates. These predicates filter the music library according to criteria you specify; to be included in the result, a media item must successfully pass through all the filters (in other words, the predicates are combined using logical-and). A predicate is a simple comparison. It has two, or possibly three, aspects:
canFilterByProperty:
).
MPMediaPredicateComparisonEqualTo
, the default) or contain the value you provide (MPMediaPredicateComparisonContains
).
These two ways of forming a query are actually the same; a convenience constructor is just a quick way of obtaining a query already endowed with a filter predicate.
A query also groups its results, according to its groupingType
. Your choices are:
MPMediaGroupingTitle
MPMediaGroupingAlbum
MPMediaGroupingArtist
MPMediaGroupingAlbumArtist
MPMediaGroupingComposer
MPMediaGroupingGenre
MPMediaGroupingPlaylist
MPMediaGroupingPodcastTitle
The query convenience constructors all supply a groupingType
in addition to a filter predicate. Indeed, the grouping is often the salient aspect of the query. For example, an albumsQuery
is in fact merely a songsQuery
with the added feature that its results are grouped by album.
The groups resulting from a query are collections; that is, each is an MPMediaItemCollection. This class, you will recall, is the superclass of MPMediaPlaylist, and is an MPMediaEntity subclass. So, a collection has properties; it also has items and a count. It also has a representativeItem
property, which gives you just one item from the collection. The reason you need this is that properties of a collection are often embodied in its items rather than in the collection itself. For example, an album has no title; rather, its items have album titles that are all the same. So to learn the title of an album, you ask for the album title of a representative item.
Unfortunately, in iOS 6, asking for a representativeItem
can result in some nasty-looking log messages: “Attempting a write transaction on a read-only database,” and “BEGIN IMMEDIATE could unexpectedly not be stepped.” A possible workaround is to use items[0]
instead of representativeItem
(even though they are not quite the same thing).
After you form the query, you perform the query. You do this simply by asking for the query’s results. You can ask either for its collections
(if you care about the groups returned from the query) or for its items
. Here, I’ll discover the titles of all the albums:
MPMediaQuery* query = [MPMediaQuery albumsQuery]; NSArray* result = [query collections]; // prove we've performed the query, by logging the album titles for (MPMediaItemCollection* album in result) NSLog(@"%@", [album.representativeItem // or album.items[0] valueForProperty:MPMediaItemPropertyAlbumTitle]); /* Output starts like this on my device: Beethoven Concertos Beethoven Overtures Etc Beethoven Piano Duet Beethoven Piano Other Beethoven Piano Sonatas ... */
Now let’s make our query more elaborate; we’ll get the titles of all the albums whose name contains “Sonata”. Observe that what we really do is to ask for all songs whose album title contains “Sonata”, grouped by album:
MPMediaQuery* query = [MPMediaQuery albumsQuery]; MPMediaPropertyPredicate* hasSonata = [MPMediaPropertyPredicate predicateWithValue:@"Sonata" forProperty:MPMediaItemPropertyAlbumTitle comparisonType:MPMediaPredicateComparisonContains]; [query addFilterPredicate:hasSonata]; NSArray* result = [query collections]; for (MPMediaItemCollection* album in result) NSLog(@"%@", [album.representativeItem // or album.items[0] valueForProperty:MPMediaItemPropertyAlbumTitle]); /* Output starts like this on my device: Beethoven Piano Sonatas Beethoven Violin Sonatas Schubert Piano Sonatas Brahms Sonatas Mozart Church Sonatas ... */
Because the results of that query are actually songs (MPMediaItems), we can immediately access any song in any of those albums. Let’s modify the output from our previous query to print the titles of all the songs in the first album returned, which happens to be the Beethoven Piano Sonatas album. We don’t have to change our query, so I’ll start at the point where we perform it:
// ... same as before ... NSArray* result = [query collections]; MPMediaItemCollection* album = result[0]; for (MPMediaItem* song in album.items) NSLog(@"%@", [song valueForProperty:MPMediaItemPropertyTitle]); /* Output starts like this on my device: Piano Sonata #1 In F Minor, Op. 2/1 - 1. Allegro Piano Sonata #1 In F Minor, Op. 2/1 - 2. Adagio Piano Sonata #1 In F Minor, Op. 2/1 - 3. Menuetto: Allegretto Piano Sonata #1 In F Minor, Op. 2/1 - 4. Prestissimo Piano Sonata #2 In A Minor, Op. 2/2 - 1. Allegro Vivace ... */
One of the properties of an MPMediaEntity is its persistent ID, which uniquely identifies this song (MPMediaItemPropertyPersistentID
) or playlist (MPMediaPlaylistPropertyPersistentID
). No other means of identification is guaranteed unique; two songs or two playlists can have the same title, for example. Using the persistent ID, you can retrieve again at a later time the same song or playlist you retrieved earlier, even across launches of your app. All sorts of things have persistent IDs — entities in general (MPMediaEntityPropertyPersistentID
), albums, artists, composers, and more.
While you are maintaining the results of a search, the contents of the music library may themselves change. For example, the user might connect the device to a computer and add or delete music with iTunes. This can put your results out of date. For this reason, the library’s own modified state is available through the MPMediaLibrary class. Call the class method defaultMediaLibrary
to get the actual library instance; now you can ask it for its lastModifiedDate
. You can also register to receive a notification, MPMediaLibraryDidChangeNotification
, when the music library is modified; this notification is not emitted unless you first send the library beginGeneratingLibraryChangeNotifications
. You should eventually balance this with endGeneratingLibraryChangeNotifications
.
New in iOS 6, a song has a property MPMediaItemPropertyIsCloudItem
, allowing you to ask whether it lives in the cloud (thanks to iTunes Match) or on the device. The distinction is clearer in than it was in iOS 5, because a song can now be played from the cloud without downloading it, and the user can manually download a song from the cloud or delete it from the device. Such changes in a song’s cloud status do not count as a change in the library.
The Media Player framework class for playing an MPMediaItem is MPMusicPlayerController. It comes in two flavors, depending on which class method you use to get an instance:
applicationMusicPlayer
applicationMusicPlayer
can be different from the Music app’s current song. This player stops when your app is not in the foreground.
iPodMusicPlayer
An applicationMusicPlayer
is not really inside your app. It is actually the global music player behaving differently. It has its own audio session. You cannot play its audio when your app is in the background. You cannot make it the target of remote control events. If these limitations prove troublesome, use the iPodMusicPlayer
(or AVPlayer, discussed later in this chapter).
A music player doesn’t merely play an item; it plays from a queue of items. This behavior is familiar from iTunes and the Music app. For example, in iTunes, when you switch to a playlist and double-click the first song to start playing, when iTunes comes to the end of that song, it proceeds by default to the next song in the playlist. So at that moment, its queue is the totality of songs in the playlist. The music player behaves the same way; when it reaches the end of a song, it proceeds to the next song in its queue.
Your methods for controlling playback also reflect this queue-based orientation. In addition to the expected play
, pause
, and stop
commands, there’s a skipToNextItem
and skipToPreviousItem
command. Anyone who has ever used iTunes or the Music app (or, for that matter, an old-fashioned iPod) will have an intuitive grasp of this and everything else a music player does. For example, you can also set a music player’s repeatMode
and shuffleMode
, just as in iTunes.
You provide a music player with its queue in one of two ways:
items
are the items of the queue.
collectionWithItems:
or initWithItems:
.
In this example, we collect all songs in the library shorter than 30 seconds into a queue and set the queue playing in random order using the application-internal music player:
MPMediaQuery* query = [MPMediaQuery songsQuery]; NSMutableArray* marr = [NSMutableArray array]; MPMediaItemCollection* queue = nil; for (MPMediaItem* song in query.items) { NSNumber* dur = [song valueForProperty:MPMediaItemPropertyPlaybackDuration]; if ([dur floatValue] < 30) [marr addObject: song]; } if ([marr count] == 0) NSLog(@"No songs that short!"); else queue = [MPMediaItemCollection collectionWithItems:marr]; if (queue) { MPMusicPlayerController* player = [MPMusicPlayerController applicationMusicPlayer]; [player setQueueWithItemCollection:queue]; player.shuffleMode = MPMusicShuffleModeSongs; [player play]; }
If a music player is currently playing, setting its queue will stop it; restarting play is up to you.
You can ask a music player for its nowPlayingItem
, and since this is an MPMediaItem, you can learn all about it through its properties. Unfortunately, you can’t query a music player as to its queue, but you can keep your own pointer to the MPMediaItemCollection constituting the queue when you hand it to the music player, and you can ask the music player for which song within the queue is currently playing (indexOfNowPlayingItem
). The user can completely change the queue of an iPodMusicPlayer
, so if control over the queue is important to you, use the applicationMusicPlayer
.
A music player has a playbackState
that you can query to learn what it’s doing (whether it is playing, paused, stopped, or seeking). It also emits notifications so you can hear about changes in its state:
MPMusicPlayerControllerPlaybackStateDidChangeNotification
MPMusicPlayerControllerNowPlayingItemDidChangeNotification
MPMusicPlayerControllerVolumeDidChangeNotification
These notifications are not emitted until you tell the music player to beginGeneratingPlaybackNotifications
. This is an instance method, so you can arrange to receive notifications from just one particular music player if you like. If you do receive notifications from both, you can distinguish them by examining the NSNotification’s object
and comparing it to each player. You should eventually balance this call with endGeneratingPlaybackNotifications
.
To illustrate, I’ll extend the previous example to set a UILabel in our interface every time a different song starts playing. Before we start the player playing, we insert these lines to generate the notifications:
[player beginGeneratingPlaybackNotifications]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changed:) name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification object:player]; self.q = queue; // retain a pointer to the queue
And here’s how we respond to those notifications:
- (void) changed: (NSNotification*) n { MPMusicPlayerController* player = [MPMusicPlayerController applicationMusicPlayer]; if ([n object] == player) { // just playing safe NSString* title = [player.nowPlayingItem valueForProperty:MPMediaItemPropertyTitle]; NSUInteger ix = player.indexOfNowPlayingItem; [self->label setText: [NSString stringWithFormat:@"%i of %i: %@", ix+1, [self.q count], title]]; } }
There’s no periodic notification as a song plays and the current playhead position advances. To get this information, you’ll have to resort to polling. This is not objectionable as long as your polling interval is reasonably sparse; your display may occasionally fall a little behind reality, but this won’t usually matter. To illustrate, let’s add to our existing example a UIProgressView (p
) showing the current percentage of the current song played by the global player. There’s no notification, so I’ll use an NSTimer and poll the state of the player every 2 seconds:
self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
When the timer fires (timerFired:
), the progress view displays the state of the currently playing item:
MPMusicPlayerController* mp = [MPMusicPlayerController applicationMusicPlayer]; MPMediaItem* item = mp.nowPlayingItem; if (!item || mp.playbackState == MPMusicPlaybackStateStopped) { self.p.hidden = YES; return; } self.p.hidden = NO; NSTimeInterval current = mp.currentPlaybackTime; NSTimeInterval total = [[item valueForProperty:MPMediaItemPropertyPlaybackDuration] doubleValue]; self.p.progress = current / total;
The applicationMusicPlayer
has no user interface, unless you count the remote playback controls (Figure 27.1); if you want the user to have controls for playing and stopping a song, you’ll have to create them yourself. The iPodMusicPlayer
has its own natural interface — the Music app.
The Media Player framework does offer a slider for setting the system output volume, along with an AirPlay route button if appropriate; this is an MPVolumeView. An MPVolumeView works only on a device — not in the Simulator. Starting in iOS 6, it is customizable similarly to a UISlider; you can set the images for the two halves of the track, the thumb, and even the AirPlay route button, for both the Normal and the Highlighted state (while the user is touching the thumb). A nice feature is that you can retrieve the MPVolumeView’s default images, so that you can base the modified images upon them. In this example, we make the left half of the track black and the right half red, and we make the thumb larger:
CGSize sz = CGSizeMake(20,20); UIGraphicsBeginImageContextWithOptions( CGSizeMake(sz.height,sz.height), NO, 0); [[UIColor blackColor] setFill]; [[UIBezierPath bezierPathWithOvalInRect: CGRectMake(0,0,sz.height,sz.height)] fill]; UIImage* im1 = UIGraphicsGetImageFromCurrentImageContext(); [[UIColor redColor] setFill]; [[UIBezierPath bezierPathWithOvalInRect: CGRectMake(0,0,sz.height,sz.height)] fill]; UIImage* im2 = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [self.vv setMinimumVolumeSliderImage: [im1 resizableImageWithCapInsets:UIEdgeInsetsMake(9,9,9,9) resizingMode:UIImageResizingModeStretch] forState:UIControlStateNormal]; [self.vv setMaximumVolumeSliderImage: [im2 resizableImageWithCapInsets:UIEdgeInsetsMake(9,9,9,9) resizingMode:UIImageResizingModeStretch] forState:UIControlStateNormal]; UIImage* thumb = [self.vv volumeThumbImageForState:UIControlStateNormal]; sz = thumb.size; sz.width +=10; sz.height += 10; UIGraphicsBeginImageContextWithOptions(sz, NO, 0); [thumb drawInRect:CGRectMake(0,0,sz.width,sz.height)]; UIImage* im3 = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [self.vv setVolumeThumbImage:im3 forState:UIControlStateNormal];
MPMusicPlayerController is convenient and simple, but it’s also simple-minded. Its audio session isn’t your audio session; the music player doesn’t really belong to you. An MPMediaItem, however, has an MPMediaItemPropertyAssetURL
key whose value is a URL suitable for forming an AVAsset. Thus, another way to play an MPMediaItem is through AV Foundation (Chapter 28).
This approach puts playback of the song into your app’s audio session and allows you to control it in response to remote control events and to play it while your app is in the background. (Of course, you can do a lot more with AV Foundation than merely to play a song from the music library. For example, you could incorporate a song, or part of a song, as the sound track to a movie.)
In this simple example, we start with an array of MPMediaItems and initiate play of those items in an AVQueuePlayer:
NSArray* arr = // array of MPMediaItem; NSMutableArray* assets = [NSMutableArray array]; for (MPMediaItem* item in arr) { AVPlayerItem* pi = [[AVPlayerItem alloc] initWithURL: [item valueForProperty:MPMediaItemPropertyAssetURL]]; [assets addObject:pi]; } self.qp = [AVQueuePlayer queuePlayerWithItems:assets]; [self.qp play];
That’s easy enough, but I have the impression, based on something said in one of the WWDC 2011 videos, that it’s not what you’re supposed to do. Instead of adding a whole batch of AVPlayerItems to an AVQueuePlayer all at once, you should add just a few AVPlayerItems to start with and then add each additional AVPlayerItem when an item finishes playing. So I’ll start out by adding just three AVPlayerItems, and use KVO to observe the AVQueuePlayer’s @"currentItem"
key:
NSArray* arr = // array of MPMediaItem; self.assets = [NSMutableArray array]; for (MPMediaItem* item in arr) { AVPlayerItem* pi = [[AVPlayerItem alloc] initWithURL: [item valueForProperty:MPMediaItemPropertyAssetURL]]; [self.assets addObject:pi]; } self->_curnum = 0; // we'll need this later self->_total = [self.assets count]; // ditto self.qp = [AVQueuePlayer queuePlayerWithItems: [self.assets objectsAtIndexes: [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,3)]]]; [self.assets removeObjectsAtIndexes: [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,3)]]; [self.qp addObserver:self forKeyPath:@"currentItem" options:0 context:nil]; [self.qp play];
The implementation of observeValueForKeyPath:...
looks like this:
AVPlayerItem* item = self.qp.currentItem; NSArray* arr = item.asset.commonMetadata; arr = [AVMetadataItem metadataItemsFromArray:arr withKey:AVMetadataCommonKeyTitle keySpace:AVMetadataKeySpaceCommon]; AVMetadataItem* met = arr[0]; [met loadValuesAsynchronouslyForKeys:@[@"value"] completionHandler:^{ dispatch_async(dispatch_get_main_queue(), ^{ self.label.text = [NSString stringWithFormat:@"%i of %i: %@", ++self->_curnum, self->_total, [met valueForKey:@"value"]]; }); }]; if (![self.assets count]) return; AVPlayerItem* newItem = self.assets[0]; [self.qp insertItem:newItem afterItem:[self.qp.items lastObject]]; [self.assets removeObjectAtIndex:0];
That code illustrates how to extract metadata from an AVAsset by way of an AVMetadataItem; in this case, we fetch the AVMetadataCommonKeyTitle and get its value, as the equivalent of fetching an MPMediaItem’s MPMediaItemPropertyTitle property in our earlier code. loadValuesAsynchronouslyForKeys:completionHandler:
is the way to retrieve a property from various AV Foundation classes, including AVMetadataItem. There are no guarantees about what thread the completion handler will be called on, so to set the label’s text, I step out to the main thread (more about that in Chapter 38).
In the last three lines, we pull an AVPlayerItem off the front of our assets
mutable array and add it to the end of the AVQueuePlayer’s queue. The AVQueuePlayer itself deletes an item from the start of its queue after playing it, so this way the queue never exceeds three items in length.
Just as in the previous example, where we updated a progress view in response to the firing of a timer to reflect an MPMusicPlayerController’s current item’s time and duration, we can do the same thing with the currently playing AVPlayerItem. Here’s the code that runs when our timer fires:
if (self.qp.rate < 0.01) self.p.hidden = YES; else { self.p.hidden = NO; AVPlayerItem* item = self.qp.currentItem; CMTime cur = self.qp.currentTime; CMTime dur = item.duration; self.p.progress = CMTimeGetSeconds(cur)/CMTimeGetSeconds(dur); }
The music picker (MPMediaPickerController) is a view controller (UIViewController) whose view is a self-contained navigation interface in which the user can select a media item. This interface looks very much like the Music app. You have no access to the actual view; you are expected to present the view controller (or, on the iPad, to use a popover).
You can limit the type of media items displayed by creating the controller using initWithMediaTypes:
. You can make a prompt appear at the top of the navigation bar (prompt
). And you can govern whether the user can choose multiple media items or just one, with the allowsPickingMultipleItems
property. New in iOS 6, you can filter out items stored in the cloud (through iTunes Match) by setting showsCloudItems
to NO. That’s all there is to it.
While the view is showing, you learn what the user is doing through two delegate methods (MPMediaPickerControllerDelegate):
mediaPicker:didPickMediaItems:
mediaPickerDidCancel:
How you use these depends on the value of the controller’s allowsPickingMultipleItems
:
allowsPickingMultipleItems
is NO (the default)
mediaPicker:didPickMediaItems:
is called, handing you an MPMediaItemCollection consisting of all items the user has tapped so far (including the same item multiple times if the user taps the same item more than once). When the user taps Cancel, your mediaPickerDidCancel:
is called.
allowsPickingMultipleItems
is YES
mediaPicker:didPickMediaItems:
is called, handing you an MPMediaItemCollection consisting of all items for which the user has tapped the Plus button (including the same item multiple times if the user taps the same item’s Plus button more than once). Your mediaPickerDidCancel:
is never called.
The view is not automatically dismissed; it is up to you to dismiss the presented view controller.
In this example, we put up the music picker, allowing the user to choose one media item; we then play that media item with the application’s music player:
- (void) presentPicker { MPMediaPickerController* picker = [MPMediaPickerController new]; picker.delegate = self; [self presentViewController:picker animated:YES completion:nil]; } - (void) mediaPicker: (MPMediaPickerController*) mediaPicker didPickMediaItems: (MPMediaItemCollection*) mediaItemCollection { MPMusicPlayerController* player = [MPMusicPlayerController applicationMusicPlayer]; [player setQueueWithItemCollection:mediaItemCollection]; [player play]; [self dismissViewControllerAnimated:YES completion:nil]; } - (void) mediaPickerDidCancel: (MPMediaPickerController*) mediaPicker { [self dismissViewControllerAnimated:YES completion:nil]; }
On the iPad, the music picker can be displayed as a presented view, but it also works very well in a popover. I’ll use this opportunity to provide a complete example (Example 29.1) of managing a single view controller as either a presented view or a popover. The presentPicker
method is now a button’s control event action handler, so that we can point the popover’s arrow to the button. How we summon the picker depends on the device; we use UI_USER_INTERFACE_IDIOM
to distinguish the two cases. If it’s an iPad, we create a popover and set an instance variable to retain it (as discussed in Chapter 22). Two methods dismiss the picker, so that operation is factored out into a utility method (dismissPicker:
) that does one thing if there’s a popover and another if there’s a presented view controller.
Example 29.1. A presented view on the iPhone, a popover on the iPad
- (void) presentPicker: (id) sender { MPMediaPickerController* picker = [MPMediaPickerController new]; picker.delegate = self; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) [self presentViewController:picker animated:YES completion:nil]; else { UIPopoverController* pop = [[UIPopoverController alloc] initWithContentViewController:picker]; self.currentPop = pop; [pop presentPopoverFromRect:[sender bounds] inView:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; pop.passthroughViews = nil; } } - (void) dismissPicker: (MPMediaPickerController*) mediaPicker { if (self.currentPop && self.currentPop.popoverVisible) { [self.currentPop dismissPopoverAnimated:YES]; } else { [self dismissViewControllerAnimated:YES completion:nil]; } } - (void)mediaPicker: (MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection { MPMusicPlayerController* player = [MPMusicPlayerController applicationMusicPlayer]; [player setQueueWithItemCollection:mediaItemCollection]; [player play]; [self dismissPicker: mediaPicker]; } - (void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker { [self dismissPicker: mediaPicker]; } - (void)popoverControllerDidDismissPopover:(UIPopoverController*)popoverController { self.currentPop = nil; }