最近朋友想做個(gè)音樂(lè)App四苇,讓我?guī)兔⒖枷乱押АF渲懈柙~動(dòng)態(tài)滾動(dòng)的效果离钝,正好我之前也沒(méi)做過(guò),順便學(xué)習(xí)一下褪储,先來(lái)個(gè)預(yù)覽效果卵渴。
實(shí)現(xiàn)思路
歌詞常見(jiàn)的就是lrc歌詞了,我們這里也是通過(guò)解析lrc歌詞文件來(lái)獲取其播放參數(shù)鲤竹,以實(shí)現(xiàn)和播放器協(xié)同浪读。下面是我從百度音樂(lè)獲取的歌詞文件示例:
```[ti:冰雨]`
[ar:劉德華]
[al:笨小孩]
[00:0.05]冰雨
[00:0.94]作詞:劉德華、李密 作曲:潘協(xié)慶
[00:01.23]演唱:劉德華
[00:01.37]
[00:04.79](歌手獨(dú)白)
[00:17.18]我是在等待 一個(gè)女孩
[00:25.18]還是在等待沉淪苦海
[00:32.91]一個(gè)人靜靜發(fā)呆 沒(méi)有人去管花謝花開(kāi)
[00:41.03]無(wú)法肯定的愛(ài) 左右搖擺
[00:45.43]只好把心酸往深心里塞
[00:49.61]我是在等待 你的回來(lái)
[00:57.73]難道只換回一句活該
[01:05.27]一個(gè)人靜靜發(fā)呆
[01:09.18]兩個(gè)人卻有不同無(wú)奈
[01:13.15]好好的一份愛(ài) 啊怎么會(huì)慢慢變壞
[01:18.43]
[01:20.44]冷冷的冰雨在臉上胡亂的拍
[01:24.31]暖暖的眼淚跟寒雨混成一塊
[01:28.32]眼前的色彩忽然被掩蓋
[01:32.28]你的影子無(wú)情在身邊徘徊
[01:36.30]你就像一個(gè)劊子手把我出賣
[01:40.35]我的心彷佛被剌刀狠狠地宰
[01:44.36]在懸崖上的愛(ài) 誰(shuí)會(huì)愿意接受最痛的意外
[01:51.09]
[02:26.59]我是在等待你的回來(lái)
[02:35.52]難道只換回一句活該
[02:42.99]一個(gè)人靜靜發(fā)呆
[02:46.99]兩個(gè)人卻有不同無(wú)奈
[02:51.08]好好的一份愛(ài) 啊怎么會(huì)慢慢變壞
[02:56.42]
[02:58.54]冷冷的冰雨在臉上胡亂的拍
[03:02.41]暖暖的眼淚跟寒雨混成一塊
[03:06.39]眼前的色彩忽然被掩蓋
[03:10.31]你的影子無(wú)情在身邊徘徊
[03:14.23]你就像一個(gè)劊子手把我出賣
[03:18.34]我的心彷佛被剌刀狠狠地宰
[03:22.33]在懸崖上的愛(ài) 誰(shuí)會(huì)愿意接受最痛的意外
[03:28.66]
[03:34.57]冷冷的冰雨在臉上胡亂的拍
[03:38.33]暖暖的眼淚跟寒雨混成一塊
[03:42.31]眼前的色彩忽然被掩蓋
[03:46.32]你的影子無(wú)情在身邊徘徊
[03:50.27]你就像一個(gè)劊子手把我出賣
[03:54.34]我的心彷佛被剌刀狠狠地宰
[03:58.34]懸崖上的愛(ài) 誰(shuí)會(huì)敢去采
[04:02.37]還是愿意接受最痛的意外 最愛(ài)的女孩
[04:08.85]
[04:19.72]懸崖上的愛(ài) 誰(shuí)會(huì)敢去采
[04:31.84]還是愿意接受最痛的意外 最愛(ài)的女孩
[04:51.75]
[05:10.60]歌手獨(dú)白
[06:16.76]```
解析lrc歌詞
這可能是最常見(jiàn)的格式了,每行為一句歌詞碘橘,[]括號(hào)內(nèi)為歌詞對(duì)應(yīng)的時(shí)間區(qū)間互订,所以我們首先要做的事情就是將他們提取分離出來(lái),分別作為時(shí)間參數(shù)數(shù)組和歌詞內(nèi)容數(shù)組痘拆。這里我參考了一些博客的方法仰禽,解析lrc文件的代碼如下:
#import <Foundation/Foundation.h> @interface LrcParser : NSObject //時(shí)間 @property (nonatomic,strong) NSMutableArray *timerArray; //歌詞 @property (nonatomic,strong) NSMutableArray *wordArray; //解析歌詞 -(void) parseLrc; //解析歌詞 -(void) parseLrc:(NSString*)lrc; @end
實(shí)現(xiàn)代碼
@implementation LrcParser -(instancetype) init{ self=[super init]; if(self!=nil){ self.timerArray=[[NSMutableArray alloc] init]; self.wordArray=[[NSMutableArray alloc] init]; } return self; }
-(NSString *)getLrcFile:(NSString *)lrc{ NSString* filePath=[[NSBundle mainBundle] pathForResource:lrc ofType:@"lrc"]; return [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; }
//測(cè)試示例 -(void)parseLrc{ [self parseLrc:[self getLrcFile:@"冰雨"]]; }
-(void)parseLrc:(NSString *)lrc{ NSLog(@"%@",lrc); if(![lrc isEqual:nil]){ NSArray *sepArray=[lrc componentsSeparatedByString:@"["]; NSArray *lineArray=[[NSArray alloc] init]; for(int i=0;i<sepArray.count;i++){ if([sepArray[i] length]>0){ lineArray=[sepArray[i] componentsSeparatedByString:@"]"]; if(![lineArray[0] isEqualToString:@"\n"]){ [self.timerArray addObject:lineArray[0]];
[self.wordArray addObject:lineArray.count>1?lineArray[1]:@""]; } } } } } @end
經(jīng)過(guò)測(cè)試,可以將歌詞順利解析出來(lái)纺蛆,下面我們要將獲得歌詞數(shù)據(jù)應(yīng)用于控制器吐葵。
實(shí)現(xiàn)動(dòng)態(tài)歌詞頁(yè)面
看了QQ音樂(lè)的滾動(dòng)歌詞頁(yè)面后,可以知道是借助UITableView或者UIScrollView來(lái)實(shí)現(xiàn)的桥氏,這里我們采用UITableView來(lái)實(shí)現(xiàn)動(dòng)態(tài)歌詞界面温峭。
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //歌詞TableView代理 self.lrcTable.delegate=self; self.lrcTable.dataSource=self; //解析歌詞 self.lrcContent=[[LrcParser alloc] init]; [self.lrcContent parseLrc]; [self.lrcTable reloadData]; //初始化播放器 [self initPlayer]; //監(jiān)聽(tīng)播放器狀態(tài) [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateTime) userInfo:nil repeats:YES]; //載入歌詞背景 UIImage *img=[UIImage imageNamed:@"wall1.jpg"]; UIImageView *bgView=[[UIImageView alloc] initWithImage:[self getBlurredImage:img]]; self.lrcTable.backgroundView=bgView; } //cell委托 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell=[self.lrcTable dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; cell.textLabel.text=self.lrcContent.wordArray[indexPath.row]; if(indexPath.row==_currentRow) cell.textLabel.textColor = [UIColor redColor]; else cell.textLabel.textColor = [UIColor whiteColor]; cell.textLabel.textAlignment = NSTextAlignmentCenter; cell.textLabel.font = [UIFont systemFontOfSize:15]; cell.backgroundColor=[UIColor clearColor]; return cell; }
這里歌詞列表的背景我采用了高斯模糊的圖片,高斯模糊的方法如下:
//實(shí)現(xiàn)高斯模糊 -(UIImage *)getBlurredImage:(UIImage *)image{ CIContext *context = [CIContext contextWithOptions:nil]; CIImage *ciImage=[CIImage imageWithCGImage:image.CGImage]; CIFilter *filter=[CIFilter filterWithName:@"CIGaussianBlur"]; [filter setValue:ciImage forKey:kCIInputImageKey]; [filter setValue:@5.0f forKey:@"inputRadius"]; CIImage *result=[filter valueForKey:kCIOutputImageKey]; CGImageRef ref=[context createCGImage:result fromRect:[result extent]]; return [UIImage imageWithCGImage:ref]; }
播放器則采用AVPlayer识颊,其定義和初始化設(shè)置如下:
@interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate> @property (nonatomic,strong) AVAudioPlayer *player; @end
-(void) initPlayer{ AVAudioSession *session=[AVAudioSession sharedInstance]; [session setActive:YES error:nil]; [session setCategory:AVAudioSessionCategoryPlayback error:nil]; [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; self.player=[[AVAudioPlayer alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"冰雨" withExtension:@"mp3"] error:nil]; //單曲循環(huán) self.player.numberOfLoops=10; [self.player prepareToPlay]; [self.player play]; }
這樣就為應(yīng)用定義了一個(gè)音樂(lè)播放器诚镰,下面要監(jiān)聽(tīng)播放器的時(shí)間參數(shù)奕坟,來(lái)載入對(duì)應(yīng)的歌詞祥款,如下:
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateTime) userInfo:nil repeats:YES];
根據(jù)時(shí)間更新UI
-(void) updateTime{ CGFloat currentTime=self.player.currentTime; NSLog(@"%d:%d",(int)currentTime / 60, (int)currentTime % 60); for (int i=0; i<self.lrcContent.timerArray.count; i++) { NSArray *timeArray=[self.lrcContent.timerArray[i] componentsSeparatedByString:@":"]; float lrcTime=[timeArray[0] intValue]*60+[timeArray[1] floatValue]; if(currentTime>lrcTime){ _currentRow=i; }else break; } [self.lrcTable reloadData]; [self.lrcTable scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_currentRow inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; }
最后編譯運(yùn)行,就會(huì)發(fā)現(xiàn)一個(gè)滾動(dòng)歌詞播放器就實(shí)現(xiàn)啦月杉。
完整Demo項(xiàng)目地址:https://github.com/ChangweiZhang/AudioPlayerDemo