DFPlayerLyricsTableview.m 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. //
  2. // DFPlayerLyricsTableview.m
  3. // DFPlayer
  4. //
  5. // Created by ihoudf on 2017/8/16.
  6. // Copyright © 2017年 ihoudf. All rights reserved.
  7. //
  8. #import "DFPlayerLyricsTableview.h"
  9. #import "DFPlayer.h"
  10. #import "DFPlayerTool.h"
  11. @interface DFPlayerLyricsTableViewCell : UITableViewCell
  12. @property (nonatomic, strong) UILabel *backgroundLrcLabel;
  13. @property (nonatomic, strong) UILabel *foregroundLrcLabel;
  14. @property (nonatomic, strong) CALayer *lrcMasklayer;
  15. @end
  16. @implementation DFPlayerLyricsTableViewCell
  17. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
  18. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  19. if (self) {
  20. self.backgroundLrcLabel = [[UILabel alloc] init];
  21. self.backgroundLrcLabel.lineBreakMode = NSLineBreakByTruncatingTail;
  22. self.backgroundLrcLabel.textAlignment = NSTextAlignmentCenter;
  23. [self.contentView addSubview:self.backgroundLrcLabel];
  24. self.foregroundLrcLabel = [[UILabel alloc] init];
  25. self.foregroundLrcLabel.lineBreakMode = NSLineBreakByTruncatingTail;
  26. self.foregroundLrcLabel.textAlignment = NSTextAlignmentCenter;
  27. [self.contentView addSubview:self.foregroundLrcLabel];
  28. self.lrcMasklayer = [CALayer layer];
  29. self.lrcMasklayer.anchorPoint = CGPointMake(0, 0.5);
  30. self.foregroundLrcLabel.layer.mask = self.lrcMasklayer;
  31. }
  32. return self;
  33. }
  34. @end
  35. static NSString *DFPlayerLyricsStateKey = @"state";
  36. static NSString *DFPlayerLyricsCurrentTimeKey = @"currentTime";
  37. static NSString *DFPlayerLyricsCurrentAudioInfoModelKey = @"currentAudioInfoModel";
  38. static NSString *DFPlayerLyricsConstMark = @"####";
  39. @interface DFPlayerLyricsTableview ()
  40. <UITableViewDelegate,UITableViewDataSource,UIScrollViewDelegate>
  41. {
  42. NSIndexPath *_currentIndexPath; // 歌词当前行IndexPath
  43. NSIndexPath *_lastIndexPath; // 歌词上一行IndexPath
  44. NSInteger _lastIndex; // 歌词上一行标记
  45. NSInteger _currentIndex; // 歌词当前行标记
  46. CGFloat _timeOffset; // 偏移时间。首次进入和拖拽后设置
  47. BOOL _isDraging; // 是否正在拖拽歌词tableView
  48. BOOL _isDecelerate; // 拖拽歌词tableView松手后tableView是否还在滚动
  49. BOOL _isSeekEnd; // 拖拽进度条是否结束
  50. }
  51. // 当前AudioUrl
  52. @property (nonatomic, strong) NSURL *audioUrl;
  53. // 时间数组
  54. @property (nonatomic, strong) NSMutableArray <NSString *> *timeArray;
  55. // 歌词数组
  56. @property (nonatomic, strong) NSMutableArray <NSString *> *lyricsArray;
  57. // 歌词frame数组——currentLineLrcFont
  58. @property (nonatomic, strong) NSMutableArray *currentLyricsFrameArray;
  59. // 歌词frame数组——otherLineLrcFont
  60. @property (nonatomic, strong) NSMutableArray *otherLyricsFrameArray;
  61. // 解析临时字典
  62. @property (nonatomic, strong) NSMutableDictionary *tempLrcDictionary;
  63. // 歌词当前行Index数组
  64. @property (nonatomic, strong) NSMutableArray *currentIndexArray;
  65. // 遮罩
  66. @property (nonatomic, strong) CALayer *maskLayer;
  67. // 等待恢复行的indexpath
  68. @property (nonatomic, strong) NSIndexPath *waitResetIndexpath;
  69. @end
  70. @implementation DFPlayerLyricsTableview
  71. - (void)dealloc{
  72. [[NSNotificationCenter defaultCenter] removeObserver:DFPlayerNotificationSeekEnd];
  73. [[DFPlayer sharedPlayer] removeObserver:self forKeyPath:DFPlayerLyricsStateKey];
  74. [[DFPlayer sharedPlayer] removeObserver:self forKeyPath:DFPlayerLyricsCurrentTimeKey];
  75. [[DFPlayer sharedPlayer] removeObserver:self forKeyPath:DFPlayerLyricsCurrentAudioInfoModelKey];
  76. }
  77. - (NSMutableArray<NSString *> *)timeArray{
  78. if (!_timeArray) {
  79. _timeArray = [NSMutableArray array];
  80. }
  81. return _timeArray;
  82. }
  83. - (NSMutableArray<NSString *> *)lyricsArray{
  84. if (!_lyricsArray) {
  85. _lyricsArray = [NSMutableArray array];
  86. }
  87. return _lyricsArray;
  88. }
  89. - (NSMutableArray *)currentLyricsFrameArray{
  90. if (!_currentLyricsFrameArray) {
  91. _currentLyricsFrameArray = [NSMutableArray array];
  92. }
  93. return _currentLyricsFrameArray;
  94. }
  95. - (NSMutableArray *)otherLyricsFrameArray{
  96. if (!_otherLyricsFrameArray) {
  97. _otherLyricsFrameArray = [NSMutableArray array];
  98. }
  99. return _otherLyricsFrameArray;
  100. }
  101. - (NSMutableDictionary *)tempLrcDictionary{
  102. if (!_tempLrcDictionary) {
  103. _tempLrcDictionary = [NSMutableDictionary dictionary];
  104. }
  105. return _tempLrcDictionary;
  106. }
  107. - (NSMutableArray *)currentIndexArray{
  108. if (!_currentIndexArray) {
  109. _currentIndexArray = [NSMutableArray array];
  110. }
  111. return _currentIndexArray;
  112. }
  113. - (instancetype)init{
  114. self = [super init];
  115. if (self) {
  116. _isDraging = NO;
  117. self.delegate = self;
  118. self.dataSource = self;
  119. self.separatorStyle = UITableViewCellSeparatorStyleNone;
  120. if (@available(iOS 11.0,*)) {
  121. self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
  122. }
  123. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(df_seekEnd) name:DFPlayerNotificationSeekEnd object:nil];
  124. NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial;
  125. [[DFPlayer sharedPlayer] addObserver:self forKeyPath:DFPlayerLyricsCurrentAudioInfoModelKey options:options context:nil];
  126. [[DFPlayer sharedPlayer] addObserver:self forKeyPath:DFPlayerLyricsCurrentTimeKey options:options context:nil];
  127. [[DFPlayer sharedPlayer] addObserver:self forKeyPath:DFPlayerLyricsStateKey options:options context:nil];
  128. }
  129. return self;
  130. }
  131. - (void)setStopUpdate:(BOOL)stopUpdate{
  132. _stopUpdate = stopUpdate;
  133. [self df_updateLyricsAnimated:YES];
  134. }
  135. - (void)df_seekEnd{
  136. _isSeekEnd = YES;
  137. [self df_updateLyricsAnimated:YES];
  138. }
  139. #pragma mark - KVO
  140. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
  141. if (object == [DFPlayer sharedPlayer]) {
  142. if ([keyPath isEqualToString:DFPlayerLyricsCurrentAudioInfoModelKey]){
  143. [self df_analyzeLyrics];
  144. }else if ([keyPath isEqualToString:DFPlayerLyricsCurrentTimeKey]){
  145. [self df_updateLyricsAnimated:YES];
  146. }else if ([keyPath isEqualToString:DFPlayerLyricsStateKey]) {
  147. if ([DFPlayer sharedPlayer].state == DFPlayerStatePlaying) {
  148. [self df_resumeLayer:self.maskLayer];
  149. }else{
  150. [self df_pauseLayer:self.maskLayer];
  151. }
  152. }
  153. }
  154. }
  155. #pragma mark - 更新歌词信息
  156. - (void)df_updateLyricsAnimated:(BOOL)animated{
  157. if (self.stopUpdate) {
  158. [self df_pauseLayer:self.maskLayer];
  159. return;
  160. }
  161. if (self.timeArray.count <= 0 || self.lyricsArray.count <= 0) {
  162. return;
  163. }
  164. _timeOffset = 0;
  165. [self.currentIndexArray removeAllObjects];
  166. BOOL scrollAnimated = _isSeekEnd || !animated;
  167. //获取当前行
  168. CGFloat currentTimeFloat = [DFPlayer sharedPlayer].currentTime;
  169. NSInteger currentTime = (NSInteger)currentTimeFloat;
  170. for (int i = 0; i < self.timeArray.count; i++) {
  171. NSInteger time = [self.timeArray[i] integerValue];
  172. if (scrollAnimated) {
  173. if (currentTime >= time) {
  174. _currentIndex = i;
  175. //获取偏移时间
  176. if (self.currentLineLrcForegroundTextColor) {
  177. _timeOffset = currentTimeFloat - [self.timeArray[i] floatValue];
  178. }
  179. }
  180. }else{
  181. if (currentTime == time) {
  182. [self.currentIndexArray addObject:[NSString stringWithFormat:@"%d",i]];
  183. _currentIndex = i;
  184. }
  185. }
  186. if (time > currentTime) {
  187. break;
  188. }
  189. }
  190. if (_lastIndex == _currentIndex) {
  191. return;
  192. }
  193. if (_lastIndex >= 0) {
  194. _lastIndexPath = [NSIndexPath indexPathForRow:_lastIndex inSection:0];
  195. }
  196. _lastIndex = _currentIndex;
  197. _currentIndexPath = [NSIndexPath indexPathForRow:_currentIndex inSection:0];
  198. if (_isSeekEnd) {//进度回滚,恢复正在播放的当前行
  199. [self setOtherLineLyricsTextStatus:_currentIndexPath];
  200. }
  201. //返回当前行歌词
  202. if (self.lyricsDelegate && [self.lyricsDelegate respondsToSelector:@selector(df_lyricsTableview:onPlayingLyrics:)]) {
  203. NSString *lyrics = self.lyricsArray[_currentIndex];
  204. if ([lyrics isEqualToString:DFPlayerLyricsConstMark]) {
  205. lyrics = @"";
  206. }
  207. dispatch_async(dispatch_get_main_queue(), ^{
  208. [self.lyricsDelegate df_lyricsTableview:self onPlayingLyrics:lyrics];
  209. });
  210. }
  211. //当前行移动到中间
  212. [self df_scrollPositionMiddleAnimated:!scrollAnimated];
  213. //刷新当前行
  214. [self updateLyricsAnimated:animated];
  215. }
  216. - (void)updateLyricsAnimated:(BOOL)animated{
  217. //同一分同一秒(只有毫秒数不同时)有两句以上歌词
  218. if (self.currentIndexArray.count > 1) {
  219. for (int i = 0; i < self.currentIndexArray.count-1; i++) {
  220. NSInteger index = [self.currentIndexArray[i] integerValue];
  221. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
  222. dispatch_async(dispatch_get_main_queue(), ^{
  223. DFPlayerLyricsTableViewCell *cell = (DFPlayerLyricsTableViewCell *)[self cellForRowAtIndexPath:indexPath];
  224. cell.foregroundLrcLabel.hidden = YES;
  225. [self setCurrentLineLyricsTextStatus:cell.backgroundLrcLabel
  226. textColor:self.currentLineLrcForegroundTextColor ? : self.currentLineLrcBackgroundTextColor];
  227. });
  228. [self performSelector:@selector(setOtherLineLyricsTextStatus:) withObject:indexPath afterDelay:0.2];
  229. }
  230. }
  231. dispatch_async(dispatch_get_main_queue(), ^{
  232. //当前行
  233. DFPlayerLyricsTableViewCell *cell = (DFPlayerLyricsTableViewCell *)[self cellForRowAtIndexPath:self->_currentIndexPath];
  234. if (!self->_isSeekEnd) {
  235. //如果当前行无歌词,记录位置并返回
  236. NSString *lyrics = [cell.foregroundLrcLabel.text df_removeEmpty];
  237. if ([lyrics df_isEmpty] || [lyrics isEqualToString:DFPlayerLyricsConstMark]) {
  238. if (self.waitResetIndexpath) {
  239. [self setOtherLineLyricsTextStatus:self.waitResetIndexpath];
  240. }
  241. self.waitResetIndexpath = self->_lastIndexPath;
  242. return;
  243. }
  244. }
  245. self->_isSeekEnd = NO;
  246. //设置其他行
  247. [self setOtherLineLyricsTextStatus:self->_lastIndexPath];
  248. //还原等待恢复行
  249. if (self.waitResetIndexpath) {
  250. [self setOtherLineLyricsTextStatus:self.waitResetIndexpath];
  251. self.waitResetIndexpath = nil;
  252. }
  253. //设置当前行
  254. cell.foregroundLrcLabel.hidden = !self.currentLineLrcForegroundTextColor;
  255. [self setCurrentLineLyricsTextStatus:cell.backgroundLrcLabel
  256. textColor:self.currentLineLrcBackgroundTextColor];
  257. //如果是卡拉OK模式
  258. if (self.currentLineLrcForegroundTextColor) {
  259. [self setCurrentLineLyricsTextStatus:cell.foregroundLrcLabel
  260. textColor:self.currentLineLrcForegroundTextColor];
  261. cell.lrcMasklayer.position = CGPointMake(0, self.cellRowHeight/2);
  262. cell.lrcMasklayer.bounds = CGRectMake(0, 0, 0, self.cellRowHeight);
  263. cell.lrcMasklayer.backgroundColor = [UIColor whiteColor].CGColor;
  264. self.maskLayer = cell.lrcMasklayer;
  265. if (self.timeArray.count == 0 || self.lyricsArray.count == 0) {//安全性判断
  266. return;
  267. }
  268. CGFloat duration = 0;
  269. if (self->_currentIndex < self.timeArray.count - 1) {
  270. duration = fabsf([self.timeArray[self->_currentIndex+1] floatValue]-[self.timeArray[self->_currentIndex] floatValue]);
  271. }else{//最后一句歌词
  272. if (![self.lyricsArray.lastObject df_isEmpty]) {//如果最后一句不为空
  273. duration = fabs([DFPlayer sharedPlayer].totalTime - [self.timeArray[self->_currentIndex] floatValue]-0.2);
  274. }
  275. }
  276. if (duration != 0) {
  277. CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"bounds.size.width"];
  278. NSNumber *end = [NSNumber numberWithFloat:CGRectGetWidth(cell.foregroundLrcLabel.frame)];
  279. anim.values = @[@(0),end];
  280. anim.keyTimes = @[@(0),@(1)];
  281. anim.duration = duration;
  282. anim.timeOffset = self->_timeOffset;
  283. anim.calculationMode = kCAAnimationLinear;
  284. anim.fillMode = kCAFillModeForwards;
  285. anim.removedOnCompletion = NO;
  286. anim.autoreverses = NO;
  287. [self.maskLayer addAnimation:anim forKey:@"Animation"];
  288. if (!animated) {
  289. self.maskLayer.speed = 0.0;
  290. }
  291. }
  292. }
  293. });
  294. }
  295. // 设置当前行歌词状态
  296. - (void)setCurrentLineLyricsTextStatus:(UILabel *)label textColor:(UIColor *)textColor{
  297. label.textColor = textColor;
  298. label.font = self.currentLineLrcFont;
  299. label.frame = [self.currentLyricsFrameArray[_currentIndexPath.row] CGRectValue];
  300. }
  301. // 设置其他行歌词状态
  302. - (void)setOtherLineLyricsTextStatus:(id)value{
  303. NSIndexPath *indexPath = (NSIndexPath *)value;
  304. DFPlayerLyricsTableViewCell *cell = (DFPlayerLyricsTableViewCell *)[self cellForRowAtIndexPath:indexPath];
  305. [self setOtherLineLyricsTextStatus:cell indexPath:indexPath];
  306. }
  307. - (void)setOtherLineLyricsTextStatus:(DFPlayerLyricsTableViewCell *)cell indexPath:(NSIndexPath *)indexPath{
  308. if (indexPath.row >= self.otherLyricsFrameArray.count) {
  309. return;
  310. }
  311. cell.foregroundLrcLabel.hidden = YES;
  312. cell.backgroundLrcLabel.textColor = self.otherLineLrcBackgroundTextColor;
  313. cell.backgroundLrcLabel.font = self.otherLineLrcFont;
  314. cell.backgroundLrcLabel.frame = [self.otherLyricsFrameArray[indexPath.row] CGRectValue];
  315. }
  316. // cell移动到当前行歌词
  317. - (void)df_scrollPositionMiddleAnimated:(BOOL)animated{
  318. if (!_isDraging && _currentIndex < self.lyricsArray.count) {
  319. dispatch_async(dispatch_get_main_queue(), ^{
  320. [self scrollToRowAtIndexPath:self->_currentIndexPath
  321. atScrollPosition:(UITableViewScrollPositionMiddle)
  322. animated:animated];
  323. });
  324. }
  325. }
  326. // 暂停恢复
  327. -(void)df_pauseLayer:(CALayer*)layer{
  328. CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
  329. layer.speed = 0.0;
  330. layer.timeOffset = pausedTime;
  331. }
  332. -(void)df_resumeLayer:(CALayer*)layer{
  333. CFTimeInterval pausedTime = [layer timeOffset];
  334. layer.speed = 1.0;
  335. layer.timeOffset = 0.0;
  336. layer.beginTime = 0.0;
  337. CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
  338. layer.beginTime = timeSincePause;
  339. }
  340. #pragma mark - tableview
  341. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
  342. return self.lyricsArray.count;
  343. }
  344. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
  345. return [self.otherLyricsFrameArray[indexPath.row] CGRectValue].size.height;
  346. }
  347. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
  348. NSString *identifier = [NSString stringWithFormat:@"DFPLayerLyricsTableViewCell%ld%ld", (long)indexPath.section, (long)indexPath.row];
  349. DFPlayerLyricsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
  350. if (!cell) {
  351. cell = [[DFPlayerLyricsTableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:identifier];
  352. cell.backgroundColor = self.cellBackgroundColor;
  353. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  354. }
  355. if (indexPath == _currentIndexPath) {//当前行
  356. cell.foregroundLrcLabel.hidden = YES;
  357. [self setCurrentLineLyricsTextStatus:cell.backgroundLrcLabel
  358. textColor:self.currentLineLrcForegroundTextColor ? : self.currentLineLrcBackgroundTextColor];
  359. }else{//其他行
  360. if (indexPath == self.waitResetIndexpath) {
  361. cell.foregroundLrcLabel.hidden = YES;
  362. cell.backgroundLrcLabel.textColor = self.currentLineLrcForegroundTextColor ? : self.currentLineLrcBackgroundTextColor;
  363. cell.backgroundLrcLabel.font = self.currentLineLrcFont;
  364. cell.backgroundLrcLabel.frame = [self.currentLyricsFrameArray[indexPath.row] CGRectValue];
  365. }else{
  366. [self setOtherLineLyricsTextStatus:cell indexPath:indexPath];
  367. }
  368. }
  369. if (indexPath.row < self.lyricsArray.count) {//安全性判断
  370. NSString *lrc = self.lyricsArray[indexPath.row];
  371. cell.hidden = [lrc df_isEmpty];
  372. if([[lrc df_removeEmpty] isEqualToString:DFPlayerLyricsConstMark]){
  373. lrc = @"";
  374. }
  375. cell.foregroundLrcLabel.text = cell.backgroundLrcLabel.text = lrc;
  376. }
  377. return cell;
  378. }
  379. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
  380. }
  381. #pragma mark - 歌词解析
  382. - (void)df_analyzeLyrics{
  383. _currentIndexPath = nil;
  384. self.waitResetIndexpath = nil;
  385. _lastIndex = -1;
  386. _currentIndex = -1;
  387. NSURL *url = [DFPlayer sharedPlayer].currentAudioModel.audioUrl;
  388. if ([self.audioUrl.absoluteString isEqualToString:url.absoluteString]) {
  389. [self checkLyricsAvailability]; // 没有换新音频时,直接复位
  390. }else{
  391. [self.tempLrcDictionary removeAllObjects];
  392. [self.timeArray removeAllObjects];
  393. [self.lyricsArray removeAllObjects];
  394. [self.currentLyricsFrameArray removeAllObjects];
  395. [self.otherLyricsFrameArray removeAllObjects];
  396. NSString *lyrics = [DFPlayer sharedPlayer].currentAudioInfoModel.audioLyrics;
  397. if (!url || [lyrics df_isEmpty]) { // 不可用时,直接复位
  398. [self checkLyricsAvailability];
  399. return;
  400. }
  401. self.audioUrl = url;
  402. [self analyzeLyrics:lyrics];//解析歌词
  403. }
  404. }
  405. - (void)analyzeLyrics:(NSString *)lyrics{
  406. //将每句歌词分割
  407. dispatch_async(DFPlayerDefaultGlobalQueue, ^{
  408. NSArray <NSString *> *arr = [lyrics componentsSeparatedByString:@"\n"];
  409. [arr enumerateObjectsUsingBlock:^(NSString * _Nonnull lrc, NSUInteger idx, BOOL * _Nonnull stop) {
  410. //如果该行为空不继续解析
  411. if ([lrc df_isEmpty]) {
  412. return;
  413. }
  414. //开始解析(这里只解析时间信息,不解析音频头部信息,如:ar:ti:等)
  415. NSArray *lineArray = [NSArray array];
  416. if ([lrc rangeOfString:@"]"].location != NSNotFound) {
  417. lineArray = [lrc componentsSeparatedByString:@"]"];
  418. if (lineArray.count > 2) {//多个时间
  419. NSMutableArray *tempTimeArray = [NSMutableArray array];
  420. for (int j = 0; j < lineArray.count - 1; j++) {
  421. CGFloat seconds = [self getLyricsTime:lineArray[j]];
  422. if (seconds >= 0) {
  423. [tempTimeArray addObject:[NSNumber numberWithFloat:seconds]];
  424. }
  425. }
  426. if (tempTimeArray.count > 0) {
  427. for (NSNumber *number in tempTimeArray) {
  428. [self addObjectWithKey:[number floatValue]
  429. value:lineArray.lastObject];
  430. }
  431. }
  432. }else{//单个时间
  433. CGFloat seconds = [self getLyricsTime:lineArray.firstObject];
  434. if (seconds >= 0) {
  435. [self addObjectWithKey:seconds
  436. value:lineArray.lastObject];
  437. }
  438. }
  439. }
  440. }];
  441. //排序
  442. [self.timeArray addObjectsFromArray:self.tempLrcDictionary.allKeys];
  443. [self.timeArray sortUsingComparator: ^NSComparisonResult (NSString *str1, NSString *str2) {
  444. return [str1 floatValue] > [str2 floatValue];
  445. }];
  446. for (NSString *key in self.timeArray) {
  447. [self.lyricsArray addObject:[self.tempLrcDictionary valueForKey:key]];
  448. }
  449. //提前计算每句歌词的frame
  450. [self getLyricsFrameArray];
  451. //重置
  452. [self checkLyricsAvailability];
  453. //得到数据调用一次更新信息
  454. [self df_updateLyricsAnimated:NO];
  455. });
  456. }
  457. // 检查是否有可用歌词。有则移动到首行
  458. - (void)checkLyricsAvailability{
  459. dispatch_async(dispatch_get_main_queue(), ^{
  460. [self reloadData];
  461. if (self.lyricsArray && self.lyricsArray.count > 0) {
  462. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
  463. [self scrollToRowAtIndexPath:indexPath atScrollPosition:(UITableViewScrollPositionTop) animated:NO];
  464. }
  465. });
  466. }
  467. // 时间转换
  468. - (CGFloat)getLyricsTime:(NSString *)time{
  469. if ([time df_isContainLetter]) {
  470. return -1;
  471. }
  472. if ([time rangeOfString:@"["].location != NSNotFound) {
  473. time = [[time componentsSeparatedByString:@"["].lastObject df_removeEmpty];
  474. }
  475. //时间转换成秒
  476. CGFloat second = -1.0;
  477. //[00:00.00]和[00:00:00](分钟:秒.毫秒)
  478. if (time.length == 8) {
  479. NSString *str = [time substringWithRange:NSMakeRange(5, 1)];
  480. if ([str isEqualToString:@":"]) {
  481. time = [time stringByReplacingOccurrencesOfString:@":" withString:@"." options:(NSAnchoredSearch) range:(NSMakeRange(5, 1))];
  482. }
  483. NSString *minutes = [time substringWithRange:NSMakeRange(0, 2)];
  484. NSString *seconds = [time substringWithRange:NSMakeRange(3, 2)];
  485. NSString *msec = [time substringWithRange:NSMakeRange(6, 2)];
  486. second = minutes.floatValue * 60 + seconds.floatValue + msec.floatValue/1000;
  487. }
  488. //[00:00](分钟:秒)
  489. if (time.length == 6) {
  490. NSString *minutes = [time substringWithRange:NSMakeRange(0, 2)];
  491. NSString *seconds = [time substringWithRange:NSMakeRange(3, 2)];
  492. second = minutes.floatValue * 60 + seconds.floatValue;
  493. }
  494. return second;
  495. }
  496. // 加入临时字典
  497. - (NSMutableDictionary *)addObjectWithKey:(CGFloat)timeKey value:(id)value{
  498. NSString *K = [NSString stringWithFormat:@"%lf",timeKey];
  499. NSString *V = [NSString stringWithFormat:@"%@",value];
  500. [self.tempLrcDictionary setValue:V forKey:K];
  501. return self.tempLrcDictionary;
  502. }
  503. #pragma mark - 计算歌词frame
  504. - (void)getLyricsFrameArray{
  505. [self.lyricsArray enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  506. dispatch_async(dispatch_get_main_queue(), ^{
  507. CGRect otherFrame = [self getLyricsFrame:obj font:self.otherLineLrcFont];
  508. [self.otherLyricsFrameArray addObject:@(otherFrame)];
  509. if (self.currentLineLrcFont == self.otherLineLrcFont) {
  510. [self.currentLyricsFrameArray addObject:@(otherFrame)];
  511. }else{
  512. CGRect currentFrame = [self getLyricsFrame:obj font:self.currentLineLrcFont];
  513. [self.currentLyricsFrameArray addObject:@(currentFrame)];
  514. }
  515. });
  516. }];
  517. }
  518. - (CGRect)getLyricsFrame:(NSString *)lyrics font:(UIFont *)font{
  519. if ([lyrics df_isEmpty]) {
  520. return CGRectZero;
  521. }
  522. CGFloat W = [lyrics boundingRectWithSize:(CGSize){MAXFLOAT, self.cellRowHeight}
  523. options:NSStringDrawingUsesLineFragmentOrigin
  524. attributes:@{NSFontAttributeName : font}
  525. context:nil].size.width;
  526. W = MIN(W, CGRectGetWidth(self.frame));
  527. CGFloat X = (CGRectGetWidth(self.frame) - W) / 2;
  528. return (CGRect){X, 0, W, self.cellRowHeight};
  529. }
  530. #pragma mark - scrollview delegate
  531. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
  532. _isDraging = YES;
  533. }
  534. - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
  535. _isDecelerate = decelerate;
  536. if (!_isDecelerate) {
  537. [self performSelector:@selector(delayToReset) withObject:nil afterDelay:1.25];
  538. }
  539. }
  540. - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
  541. if (_isDecelerate) {
  542. [self performSelector:@selector(delayToReset) withObject:nil afterDelay:1.25];
  543. }
  544. }
  545. - (void)delayToReset{
  546. _isDraging = NO;
  547. [self df_scrollPositionMiddleAnimated:YES];
  548. }
  549. @end