上一篇講了歌曲的切換,這一篇主要講歌詞部分的實現。
先看效果圖。當歌手唱到這句歌詞時候,我們要標記出來,這里顯示字體為黃色。
1.獲取歌詞
一般歌詞都是一個鏈接。類似於“http://musicdata.baidu.com/data2/lrc/131707548/131707548.lrc” ,我們從服務器下載下來,顯示到頁面上。每句歌詞后面都有“\n”,前面都有時間。這個鏈接有可能直接下載,看不到下面的效果。換個瀏覽器多試幾次就可以了,盡量使用谷歌瀏覽器。
#pragma mark - 獲取歌詞
- (void)getAlbumLrc {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 異步並發下載歌曲
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:_currentModel.lrclink]];
// 二進制轉為字符串
NSString *allLrcStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// 分割字符串
NSArray *lrcArray = [allLrcStr componentsSeparatedByString:@"\n"];
// 添加到數組中
[self.lrcArr removeAllObjects];
for (NSString *lrc in lrcArray) {
FHLrcModel *lrcModel = [FHLrcModel allocLrcModelWithLrc:lrc];
[self.lrcArr addObject:lrcModel];
}
// 主線程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
[_lrcTableView reloadData];
});
});
}
我用FHLrcModel管理每句歌詞。
FHLrcModel.h
#import <Foundation/Foundation.h>
@interface FHLrcModel : NSObject
@property (nonatomic, copy) NSString *lrc; // 歌詞
@property (nonatomic, assign) int presenTime; //顯示這句歌詞的時間
@property (nonatomic, assign) bool isPresent; // 當前顯示的是否是這句歌詞
// 實例化方法
+ (instancetype)allocLrcModelWithLrc: (NSString *)lrc;
@end
FHLrcModel.m
#import "FHLrcModel.h"
@implementation FHLrcModel
+ (instancetype)allocLrcModelWithLrc: (NSString *)lrc {
FHLrcModel *model =[FHLrcModel new];
// 把歌詞和時間分割開
NSArray *array = [lrc componentsSeparatedByString:@"]"];
// 處理時間 [00:01.70] =》 1.70
NSString *timeStr;
if ([array[0] length] >8) {
timeStr = [array[0] substringWithRange:NSMakeRange(1, 8)];
}
NSArray *timeArr = [timeStr componentsSeparatedByString:@":"];
if (timeArr.count > 1) {
model.presenTime = (int)[timeArr[0] floatValue] * 60 + [timeArr[1] intValue];
}
// 如果沒有歌詞 就換行
if (array.count > 1) {
model.lrc = array[1];
}else {
model.lrc = @"\n";
}
return model;
}
@end
2.顯示歌詞
顯示歌詞顯然用UITableView了,這個很簡單。實例化UITableView的方法我就不貼出來了,我只貼它的代理部分的代碼。
#pragma mark - UITableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.lrcArr.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [_lrcTableView dequeueReusableCellWithIdentifier:@"LrcCell" forIndexPath:indexPath];
// 去掉點擊效果
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.backgroundColor = [UIColor clearColor];
FHLrcModel *lrcmodel = self.lrcArr[indexPath.row];
cell.textLabel.text = lrcmodel.lrc;
cell.textLabel.textAlignment = NSTextAlignmentCenter;
if (lrcmodel.isPresent) {
// 當前歌詞顯示黃色
cell.textLabel.textColor = [UIColor yellowColor];
}else {
cell.textLabel.textColor = [UIColor whiteColor];
}
return cell;
}
3.同步歌詞
這部分挺有難度。我走了很多彎路。后來我想了一個辦法,當前播放時間和歌詞數組里的第一個元素去比較,比較成功了,再去比較第二個元素。這樣就避免了用for循環每一次都比較全部,這樣節省內存和時間。這樣做是因為我發現了歌詞的時間從上往下是不斷增加的,沒有歌詞復用的情況。至於怎么監聽播放進度可以看我前面的播放。
#pragma mark - 添加監聽播放進度的觀察者
- (void)addTimePlayProgerssObserver {
__block UISlider *weakPregressSlider = _playerSlider;
__weak UILabel *waekCurrentLabel = _currentLabel;
__block int weakRow = _row;// 來記錄當前歌詞顯示到第幾行
__weak typeof(self) weakSelf = self;
self.timePlayProgerssObserver = [self.avPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
// 當前播放的時間
float current = CMTimeGetSeconds(time);
// ***更新歌詞***
if (weakRow < weakSelf.lrcArr.count) {
FHLrcModel *model = weakSelf.lrcArr[weakRow];
// 比較時間 比較成功了刷新TabelView
if (model.presenTime == (int)current) {
[weakSelf reloadTabelViewWithRow:weakRow];
weakRow++;
}
}
// 總時間
float total = CMTimeGetSeconds(weakSelf.avPlayer.currentItem.duration);
// 更改當前播放時間
NSString *currentSStr = [weakSelf FormatTime: (int)current % 60];
waekCurrentLabel.text = [NSString stringWithFormat:@"%d:%@",(int)current / 60,currentSStr];
// 更新播放進度條
weakPregressSlider.value = current / total;
}];
}
備注: 主要看更新歌詞部分,其他可以略過。
#pragma mark - 更新歌詞
- (void)reloadTabelViewWithRow:(int)row {
// 找到cell
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
UITableViewCell *cell = [_lrcTableView cellForRowAtIndexPath:indexPath];
// 字體變色
cell.textLabel.textColor = [UIColor yellowColor];
// 當前歌詞滑動到TableView中間
[_lrcTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
// 上一句變為白色 如果是第一句就沒有上一句,所以不操作
if (row > 0) {
UITableViewCell *preCell = [_lrcTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:row - 1 inSection:0]];
preCell.textLabel.textColor = [UIColor whiteColor];
}
}
這樣就可以把當前演唱的歌詞實時標記出來了,還可以隨着演唱往上滑動。這個一定有其他的方法,大家可以和我探討,只有交流才能知道自己的不足,才能不斷進步。項目地址GitUp ,歡迎下載。