后台播放配置
1、打开项目的Capabilities中的Background Models 勾选第一个选项
2、设置音乐后台播放的会话类型,开启远程事件(锁屏时使用)
//设置音乐后台播放的会话类型
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
[session setCategory:AVAudioSessionCategoryPlayback error:&error];
if (error) {
NSLog(@"%s %@", __func__, error);
}
[session setActive:YES error:nil];
//开启远程事件(锁屏时使用)
[application beginReceivingRemoteControlEvents];
3、实现远程事件回调方法
#pragma mark 接收远程事件
-(void)remoteControlReceivedWithEvent:(UIEvent *)event
{
//判断是否为远程事件
if (event.type == UIEventTypeRemoteControl) {
//调用block
self.myRemoteEventBlock(event);
}
}
4、处理回调block
//锁屏后 事件
myAppDel.myRemoteEventBlock = ^(UIEvent *event)
{
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:
[[MusicPlayTool sharedMusicPlayTool] play];
break;
case UIEventSubtypeRemoteControlPause:
[[MusicPlayTool sharedMusicPlayTool] pause];
break;
case UIEventSubtypeRemoteControlPreviousTrack:
[[MusicPlayTool sharedMusicPlayTool] prepareForPlayWithMusic:[[MusicManager sharedMusicManager] upMusicInfo]];
[[MusicPlayTool sharedMusicPlayTool] play];
break;
case UIEventSubtypeRemoteControlNextTrack:
[[MusicPlayTool sharedMusicPlayTool] prepareForPlayWithMusic:[[MusicManager sharedMusicManager] nextMusicInfo]];
[[MusicPlayTool sharedMusicPlayTool] play];
break;
default:
break;
}
};
经过上面四步,app能后台播放音乐,并且能捕捉上一首歌、下一首歌、暂停和播放事件。但是锁屏的时候并没有相关的音乐的信息。运行效果如下图。
设置锁屏信息
NSMutableDictionary *info = [NSMutableDictionary dictionary];
//设置专辑名称
info[MPMediaItemPropertyAlbumTitle] = @"网络热曲";
//设置歌曲名
info[MPMediaItemPropertyTitle] = music.musicName;
//设置歌手
info[MPMediaItemPropertyArtist] = music.songer;
//设置专辑图片
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"song_400"]];
info[MPMediaItemPropertyArtwork] = artwork;
//设置歌曲时间
info[MPMediaItemPropertyPlaybackDuration] = @(self.player.duration);
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = info;
运行效果
歌词解析、歌词同步
目前的歌词都是lrc文件,格式如些
[ti:为情所伤]
[ar:庄心妍]
[al:]
[00:00.00]为情所伤
[00:02.00]演唱:庄心妍
[00:04.00]作词:镜喜 作曲:祁隆
[00:06.00]编曲:颜小健
[00:08.00]混音:王路遥
[00:10.00]音乐总监:祁隆
[00:12.00]
[00:20.02]你说不会在爱情里犯错
[00:23.83]也说过会永远的爱我
[00:27.60]才发现只剩下虚伪的承诺
[00:31.79]用泪水把回忆包裹
[00:35.41]我也曾经想把爱去闪躲
[00:39.22]可摆脱不了那份寂寞
[00:42.94]结果都是让我一错再错
[00:47.06]被伤过的心再次漂泊
[00:50.48]为什么爱留下难忍的伤
[00:54.19]心里的酸楚要去遗忘
[00:58.25]看见的是一道封闭的墙
[01:02.05]什么时候能打开一扇窗
[01:05.73]为什么爱留下难忍的伤
[01:09.53]泪水无情拍打着海浪
[01:13.38]谁能给我一双温暖的浆
[01:17.57]不让我 再为情所伤
[01:24.04]
[01:39.91]你说不会在爱情里犯错
[01:43.90]也说过会永远的爱我
[01:47.64]才发现只剩下虚伪的承诺
[01:51.83]用泪水把回忆包裹
[01:55.26]我也曾经想把爱去闪躲
[01:59.23]可摆脱不了那份寂寞
[02:02.98]结果都是让我一错再错
[02:07.08]被伤过的心再次漂泊
[02:10.50]为什么爱留下难忍的伤
[02:14.17]心里的酸楚要去遗忘
[02:18.11]看见的是一道封闭的墙
[02:21.96]什么时候能打开一扇窗
[02:25.69]为什么爱留下难忍的伤
[02:29.43]泪水无情拍打着海浪
[02:33.38]谁能给我一双温暖的浆
[02:37.32]不让我 再为情所伤
[02:40.89]为什么爱留下难忍的伤
[02:44.68]心里的酸楚要去遗忘
[02:48.65]看见的是一道封闭的墙
[02:52.48]什么时候能打开一扇窗
[02:56.19]为什么爱留下难忍的伤
[03:00.01]泪水无情拍打着海浪
[03:03.94]谁能给我一双温暖的浆
[03:07.97]不让我 再为情所伤
[03:14.57]
为了简单,不考虑上面的[ti:为情所伤]、[ar:庄心妍]、[al:]这个快内容。其余下面的部分都是采用时间+当前时间开始的歌词。那么解析思路就是把一句的时间(转换成秒)跟当前的歌词分离出来然后用字典保存,由于字典是无序的,所以再用一个数字一次保存当前的时间。代码如下:
//解析歌词
MusicInfo *currentModel = [MusicManager sharedMusicManager].currentMusic;
[self.lrcTimeArr removeAllObjects];
[self.lrcDic removeAllObjects];
if (currentModel.lrc.length>0) {
NSString *str = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:currentModel.lrc ofType:nil] encoding:NSUTF8StringEncoding error:nil];
NSArray *allRowArr = [str componentsSeparatedByString:@"\n"];
for (int i = 0; i < allRowArr.count; i++) {
NSString *linStr = [allRowArr objectAtIndex:i];
NSArray *lineArray = [linStr componentsSeparatedByString:@"]"];
if ([lineArray[0] length] > 4) {
NSString *str1 = [linStr substringWithRange:NSMakeRange(3, 1)];
NSString *str2 = [linStr substringWithRange:NSMakeRange(6, 1)];
NSString *str3 = [linStr substringWithRange:NSMakeRange(1, 1)];
if (([str1 isEqualToString:@":"] && [str2 isEqualToString:@"." ]) || ([str1 isEqualToString:@":"]&&[str2 isEqualToString:@"]"]&&[str3 isEqualToString:@"0"])) {
NSString *lrcStr = [lineArray objectAtIndex:1];
NSString *timeStr = [[lineArray objectAtIndex:0] substringWithRange:NSMakeRange(1, 5)];//分割区间求歌词时间
NSArray *array = [timeStr componentsSeparatedByString:@":"];//把时间转换成秒
NSUInteger currentTime = [array[0] intValue] * 60 + [array[1] intValue];
NSString *str = [NSString stringWithFormat:@"%ld",currentTime];
//把时间 和 歌词 加入词典
[self.lrcDic setObject:lrcStr forKey:str];
[self.lrcTimeArr addObject:str];//timeArray的count就是行数
}
}
}
}
当音乐播放时,不断刷新播放进度条的时候,根据当前的currentTime与保存时间的数字里面的时间进行对比选出当前正在播放的时间段,然后通过字典取出当前的歌词,刷新tableView,代码如下:
- (void)upDateUI
{
MusicPlayTool *playTool = [MusicPlayTool sharedMusicPlayTool];
self.currentTime.text = [NSString stringWithFormat:@"%02d:%02d",(int)playTool.player.currentTime/60,(int)playTool.player.currentTime%60];
self.progressSlider.value = playTool.player.currentTime;
for (int i = 0; i < self.lrcTimeArr.count; i++) {
NSString *timeStr = self.lrcTimeArr[i];
if ([timeStr integerValue] == (NSInteger)playTool.player.currentTime) {
_currentIndex = i;
[self resetLockInfo];
[_lrcTableView reloadData];
[_lrcTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_currentIndex inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
return;
}
}
}
实际运行效果如下图:
锁屏歌词同步
在设置锁屏信息的时候有这么一句代码:
//设置专辑图片
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"song_400"]];
info[MPMediaItemPropertyArtwork] = artwork;
图片“song_400”就是锁屏的是锁屏时中间显示的大图,要显示歌词就要将歌词绘制到图片上面,然后合成生成新的图片显示,代码如下:
//设置专辑图片
//UIImage *image = [UIImage imageNamed:music.icon];
UIImage *image = [self createShareImage:[self.lrcDic objectForKey:self.lrcTimeArr[_currentIndex]] name:@"song_400" number:nil grade:nil];
- ( UIImage *)createShareImage:( NSString *)str name:( NSString *)name number:( NSString *)number grade:( NSString *)grade
{
UIImage *image = [UIImage imageNamed:name];
CGSize size= CGSizeMake (image.size.width, image.size.height); // 画布大小
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
[image drawAtPoint:CGPointMake(0, 0)];
// 获得一个位图图形上下文
CGContextRef context= UIGraphicsGetCurrentContext ();
CGContextDrawPath(context, kCGPathStroke );
CGSize needSize = CGSizeMake(1000,20);
NSDictionary *attribute = @{NSFontAttributeName:[UIFont systemFontOfSize:16], NSForegroundColorAttributeName:[UIColor grayColor]};
CGSize labelsize = [str boundingRectWithSize:needSize options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attribute context:nil].size;
CGFloat width = MIN(labelsize.width, image.size.width);
[str drawInRect:CGRectMake((image.size.width-width)/2, image.size.height - 40, width, 20) withAttributes:attribute];
// 返回绘制的新图形
UIImage *newImage= UIGraphicsGetImageFromCurrentImageContext ();
UIGraphicsEndImageContext ();
return newImage;
}
然后在刷新进度条的时候不停的合成生成新的图片进行显示。
最终运行效果如下: