iOS仿網易雲音樂

QuintGao發表於2017-10-16

前言

    最近做專案,遇到了需要播放網路音訊的功能,由於以前對於音訊方面的至少了解的不是很多,於是通過查閱資料對音訊方面做了一些學習,然後利用VLCKit仿照網易雲音樂的播放介面寫了個Demo,在此記錄一下,大神勿噴。

    關於VLCKit,它是一款功能強大的全平臺播放器,幾乎支援所有格式的音訊、視訊檔案的播放
整合方式
1、 按照wiki的說明去自己編譯:[wiki.videolan.org/iOSCompile]
2、cocoapods方式
通過pos search MobileVLCKit去搜尋相關的庫,會發現有好幾個庫,我最終選擇了MobileVLCKit-unstable(因為這個庫更新的多,而且還在不時的更新)

pod 'MobileVLCKit-unstable', '~> 3.0.0a42'複製程式碼

說明

本Demo主要實現的有以下功能:

* 播放網路音訊、歌曲
* 唱片(播放時旋轉、左右滑動切換歌曲)
* 歌詞滾動、音量控制、歌曲切換
* 設定迴圈型別、上一曲、下一曲、喜歡歌曲等
* 鎖屏控制(播放、暫停、喜歡、上一曲、下一曲、播放條拖動)
* 耳機線控(播放、暫停、上一曲、下一曲、快進、快退)
* 通知監聽(插拔耳機、播放打斷)
* 支援AirPlay複製程式碼

不足:

* 不能獲取緩衝進度(播放庫的問題)
* 不支援本地快取(每次播放都需要網路請求)複製程式碼

demo中的音樂資料來自百度音樂,僅供學習使用,請勿在商業中使用

實現

1、播放

demo中對VLCKit實現了二次封裝GKPlayer,主要實現的有播放、暫停、停止,以及播放狀態、進度、時間等的回撥,這裡就不貼程式碼了,具體可到Demo中檢視GKPlayer。

2、轉盤

歌曲播放是需要實現轉盤無限旋轉的效果,這裡用到了CADisplayLink定時器不斷的重新整理,然後通過設定檢視的transform來實現
首先建立CADisplayLink

self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animation)];
// 加入到主迴圈中
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];複製程式碼

監聽方法中改變檢視的transform

self.diskView.transform = CGAffineTransformRotate(self.diskView.transform, M_PI_4 / 100);複製程式碼

這樣就做到了檢視的無限旋轉,而且側滑返回的時候也不會停止,下面看看效果圖

轉盤旋轉
轉盤旋轉

轉盤左右滑動切換歌曲也比較簡單,用到了UIScrollView,然後再上面放了三個轉盤檢視,通過監聽UIScrollView的滾動來切換歌曲,看看效果圖吧。

轉盤滾動
轉盤滾動

3、歌詞

對於歌詞首先當然是解析歌詞了,通過換行符\n來分離每行的歌詞,然後再通過正規表示式,分離歌詞的時間與內容。

/**
 解析歌詞方法

 @param lyricString 歌詞對應的字串
 @param isDelBlank  是否去掉空白行歌詞
 @return 歌詞解析後的模型陣列
 */
+ (NSArray *)lyricParaseWithLyricString:(NSString *)lyricString isDelBlank:(BOOL)isDelBlank {
    // 1. 以\n分割歌詞
    NSArray *linesArray = [lyricString componentsSeparatedByString:@"\n"];

    // 2. 建立模型陣列
    NSMutableArray *modelArray = [NSMutableArray new];

    // 3. 開始解析
    // 由於有形如
    // [ti:如果沒有你]
    // [00:00.64]歌詞
    // [00:01.89][03:01.23][05:03.43]歌詞
    // [00:00.8]
    // 這樣的歌詞形式,所以最好的方法是用正規表示式匹配 [00:00.00] 來獲取時間

    for (NSString *line in linesArray) {
        // 正規表示式
        NSString *pattern = @"\\[[0-9][0-9]:[0-9][0-9].[0-9]{1,}\\]";

        NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil];
        // 進行匹配
        NSArray *matchesArray = [regular matchesInString:line options:NSMatchingReportProgress range:NSMakeRange(0, line.length)];

        // 獲取歌詞部分
        // 方法一
        //        NSTextCheckingResult *match = matchesArray.lastObject;
        //
        //        NSString *content = [line substringFromIndex:(match.range.location + match.range.length)];

        // 方法二  [00:01.78]歌詞
        NSString *content = [line componentsSeparatedByString:@"]"].lastObject;

        // 獲取時間部分[00:00.00]
        for (NSTextCheckingResult *match in matchesArray) {
            NSString *timeStr = [line substringWithRange:match.range];

            // 去掉開頭和結尾的[],得到時間00:00.00

            // 去掉[
            timeStr = [timeStr substringFromIndex:1];
            // 去掉]
            timeStr = [timeStr substringToIndex:(timeStr.length - 1)];

            // 分、秒、毫秒
            NSString *minStr = [timeStr substringWithRange:NSMakeRange(0, 2)];
            NSString *secStr = [timeStr substringWithRange:NSMakeRange(3, 2)];

            // 由於毫秒有一位或者兩位,所以應從小數點(第六位)後獲取
            NSString *mseStr = [timeStr substringFromIndex:6];

            // 轉換成以毫秒秒為單位的時間 1秒 = 1000毫秒
            NSTimeInterval time = [minStr floatValue] * 60 * 1000 + [secStr floatValue] * 1000 + [mseStr floatValue];

            // 建立模型,賦值
            GKLyricModel *lyricModel = [GKLyricModel new];
            lyricModel.content      = content;
            lyricModel.msTime       = time;
            lyricModel.secTime      = time / 1000;
            lyricModel.timeString   = [GKTool timeStrWithMsTime:time];
            [modelArray addObject:lyricModel];
        }
    }

    // 去掉空白行歌詞
    if (isDelBlank) {
        [modelArray enumerateObjectsUsingBlock:^(GKLyricModel *obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if (!obj.content || [obj.content isEqualToString:@""]) {
                [modelArray removeObject:obj];
            }
        }];
    }

    // 陣列根據時間進行排序 時間(time)
    // ascending: 是否升序
    NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"msTime" ascending:YES];

    return [modelArray sortedArrayUsingDescriptors:@[descriptor]];
}複製程式碼

然後就是歌曲播放是,滾動歌詞了,這裡通過在UITableView中前後都加上5行的空白行,讓歌詞能夠顯示在中間,然後根據歌曲播放的時間,重新整理tableview選中當前時間對應的歌詞並顯示在tableview的中間。

/**
 根據當前時間及總時間滾動歌詞

 @param currentTime 當前時間
 @param totalTime 總時間
 */
- (void)scrollLyricWithCurrentTime:(NSTimeInterval)currentTime totalTime:(NSTimeInterval)totalTime {
    if (self.lyrics.count == 0) self.lyricIndex = 0;

    for (NSInteger i = 0; i < self.lyrics.count; i++) {
        GKLyricModel *currentLyric = self.lyrics[i];
        GKLyricModel *nextLyric    = nil;

        if (i < self.lyrics.count - 1) {
            nextLyric = self.lyrics[i + 1];
        }

        if ((self.lyricIndex != i && currentTime > currentLyric.msTime) && (!nextLyric || currentTime < nextLyric.msTime)) {
            self.lyricIndex = i;

            //刷表
            [self.lyricTable reloadData];

            // 不是由拖拽產生的滾動,自動滾滾動歌詞
            if (!self.isScrolling) {

                NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(self.lyricIndex + 5) inSection:0];

                [self.lyricTable selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionMiddle];
            }
        }
    }
}複製程式碼

看看效果圖吧

歌詞
歌詞

其他效果圖

鎖屏
鎖屏

當然還有很多的細節處理,在這裡就不在贅述了。具體實現還是去看Demo吧。

小結

本demo中其實並沒有音視訊的底層的知識,只是做了對VLCKit的使用,介面也是完全仿照網易雲音樂做的,這裡只是做一下分享,如果有寫的不好的地方還請見諒。

最後,如果您覺得還不錯,點個star吧!???

github地址:GKAudioPlayerDemo

簡書地址:
iOS-VLCKit實現仿網易雲音樂播放音樂(一)

iOS-VLCKit實現仿網易雲音樂播放音樂(二)

iOS-VLCKit實現仿網易雲音樂播放音樂(三)

相關文章