iOS 音乐播放、后台播放、锁屏封面、控制

一、后台播放

设置App的plist,使app可以在后台播放音乐。 Info.plist中添加UIBackgroundModes键值,添加子键值为audio。

然后再程序中添加入下代码:

AVAudioSession *session = [AVAudioSession sharedInstance];  
[session setActive:YES error:nil]; 
[session setCategory:AVAudioSessionCategoryPlayback error:nil]; 
AVAudioSession *session = [AVAudioSession sharedInstance];  
[session setActive:YES error:nil]; 
[session setCategory:AVAudioSessionCategoryPlayback error:nil]; 
二、添加播放控制器(Remote Control Events)

首先我们要告诉系统,我要接受系统的播放控制消息,这样系统才会给我们发送播放控制命令。流程是这样的:

App启动 -> 告诉系统我需要接受播放控制消息 -> 等待 -> 用户点击系统播放控制器按钮 -> 系统传递消息给App -> 我们接受到消息,做出相应的响应。

想要接收播放控制消息,我们必须要做三件事:

  • 成为Frist Responder
  • 请求系统,要求开始监听播放控制消息(Remote Control Events)
  • 开始播放音频。

请注意第三点,我们的App必须在开始播放音频后,才能收到控制消息。否则,即使你满足了前两点,也无法接收到控制消息。

//AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
     //...
     //告诉系统,我们要接受远程控制事件
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

//响应远程音乐播放控制消息
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {

    if (receivedEvent.type == UIEventTypeRemoteControl) {

        switch (receivedEvent.subtype) {

            case UIEventSubtypeRemoteControlTogglePlayPause:
                [[PlayController sharedInstance] pause];
                NSLog(@"RemoteControlEvents: pause");
                break;
            case UIEventSubtypeRemoteControlNextTrack:
                [[PlayController sharedInstance] playModeNext];
                NSLog(@"RemoteControlEvents: playModeNext");
                break;
            case UIEventSubtypeRemoteControlPreviousTrack:
                [[PlayController sharedInstance] playPrev];
                NSLog(@"RemoteControlEvents: playPrev");
                break;
            default:
                break;
        }
    }
}
//AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
     //...
     //告诉系统,我们要接受远程控制事件
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

//响应远程音乐播放控制消息
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {

    if (receivedEvent.type == UIEventTypeRemoteControl) {

        switch (receivedEvent.subtype) {

            case UIEventSubtypeRemoteControlTogglePlayPause:
                [[PlayController sharedInstance] pause];
                NSLog(@"RemoteControlEvents: pause");
                break;
            case UIEventSubtypeRemoteControlNextTrack:
                [[PlayController sharedInstance] playModeNext];
                NSLog(@"RemoteControlEvents: playModeNext");
                break;
            case UIEventSubtypeRemoteControlPreviousTrack:
                [[PlayController sharedInstance] playPrev];
                NSLog(@"RemoteControlEvents: playPrev");
                break;
            default:
                break;
        }
    }
}

播放音频的代码,这里给出一段简单的示例:

- (void)playBtnClicked
{
    NSError *error = nil;
    NSString *path = [[NSBundle mainBundle] pathForResource:@"music" ofType:@"mp3"];
    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:path] error:&error];
    if (error) {
        NSLog(@"Error:%@", [error localizedDescription]);
    }
    [player play];
}

- (void)playBtnClicked
{
    NSError *error = nil;
    NSString *path = [[NSBundle mainBundle] pathForResource:@"music" ofType:@"mp3"];
    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:path] error:&error];
    if (error) {
        NSLog(@"Error:%@", [error localizedDescription]);
    }
    [player play];
}

在开始播放音频后,使用耳机线控的播放暂停等按键,或者锁屏封面上的播放控制按键,就能够收到控制消息了。

关于耳机线控的一点说明

苹果耳机的线控上有三个按钮:加号,中部,减号。其中加号和减号是用于控制音量,这两个按钮点击是收不到消息的——UIEventSubtype没有音量改变的事件类型。而中部按钮的点击,是可以收到消息的,按一下是播放/暂停切换,快按两下是播放下一首,快按三下是播放上一首,快按两下并摁住是快进,快按三下并摁住是快退。

三、在锁屏界面显示播放歌曲信息

代码如下,其实就是设置一个全局变量的值,当系统处于音乐播放状态时,锁屏界面就会将NowPlayingInfo中的信息展示出来。可惜的是,这里的定制性不是太强,例如歌曲图片无法平铺整个屏幕大小,根据我的测试,歌曲图片在320×320时,可以完整显示在屏幕中央位置,两侧不会留下黑边。

- (void)setLockScreenNowPlayingInfo
{
     //更新锁屏时的歌曲信息
    if (NSClassFromString(@"MPNowPlayingInfoCenter")) {
        NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];

        [dict setObject:@"歌曲名" forKey:MPMediaItemPropertyTitle];
        [dict setObject:@"演唱者" forKey:MPMediaItemPropertyArtist];
        [dict setObject:@"专辑名" forKey:MPMediaItemPropertyAlbumTitle];

        UIImage *newImage = [UIImage imageWithNamed:@"歌曲封面图片"];
        [dict setObject:[[[MPMediaItemArtwork alloc] initWithImage:newImage] autorelease]
                     forKey:MPMediaItemPropertyArtwork];

        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
    }
}

- (void)setLockScreenNowPlayingInfo
{
     //更新锁屏时的歌曲信息
    if (NSClassFromString(@"MPNowPlayingInfoCenter")) {
        NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];

        [dict setObject:@"歌曲名" forKey:MPMediaItemPropertyTitle];
        [dict setObject:@"演唱者" forKey:MPMediaItemPropertyArtist];
        [dict setObject:@"专辑名" forKey:MPMediaItemPropertyAlbumTitle];

        UIImage *newImage = [UIImage imageWithNamed:@"歌曲封面图片"];
        [dict setObject:[[[MPMediaItemArtwork alloc] initWithImage:newImage] autorelease]
                     forKey:MPMediaItemPropertyArtwork];

        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
    }
}

经过了如上配置后,程序应该就能够正常显示了