iOS仿网易云音乐

6,619 阅读5分钟

前言

    最近做项目,遇到了需要播放网络音频的功能,由于以前对于音频方面的至少了解的不是很多,于是通过查阅资料对音频方面做了一些学习,然后利用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实现仿网易云音乐播放音乐(三)