audio标签自制音乐播放器,丹麦乐团

2019-12-01 04:55栏目:明星八卦
TAG:

不是乐评。

      昨天晚上在网易云音乐上给丹麦乐团《乒乓》的歌词做滚动,做歌词的时候听了好几遍,便对这个乐队愈加有好感!

@(iOS Study)[音视频播放]

声明:此文章版权归饥人谷和本人所有,转载须得到本人同意,项目为开源项目,不能用于商业应用,仅供学习。

感谢开源,所以看到优秀的代码就拿出来与大家分享。

昨天一大早微博就炸了锅,铺天盖地都是局长新专,我关注的主要是欧美音乐博主。

      首先,认识这个indie乐团是一次在云音乐上偶然听到了《那不是我》和《樱木花道》,曲风很清新,而且相对于主唱软软的声音,鼓点和吉他更加吸引我的耳朵,于是又多听了几首,旋律就跟了我一个晚上!

  • 作者: Liwx
  • 邮箱: 1032282633@qq.com

预览地址: https://boloog.github.io/music/
GitHub地址: https://github.com/boloog/music/

效果图

www.55402.com 1music_gif.gifwww.55402.com 2music_1.pngwww.55402.com 3music_2.pngwww.55402.com 4music_3.pngwww.55402.com 5music_4.pngwww.55402.com 6music_5.pngwww.55402.com 7music_6.pngwww.55402.com 8music_7.pngwww.55402.com 9music_8.png

  • 源码链接

然后我第一时间去music搜,没有,再次tidal独家。骗钱两口子果然够狠。

      翻了下鹿包的微博,是很可爱的妹子---在《乒乓》中把“来啦来啦来啦来啦”唱成“啦啦啦啦啦啦啦啦”的可爱的妹子


相关技能

  • HTML5 CSS3(实现页面布局和动态效果)
  • Iconfont(使用矢量图标库添加播放器相关图标)
  • LESS (动态CSS编写)
  • jQuery(快速编写js脚本)
  • gulp webpack(自动化构建工具,实现LESS,CSS,JS等编译和压缩代码)

项目简单介绍

  • 一款优雅的遵循 Material Design 的开源音乐播放器,UI参考 腾讯轻听 音乐播放器,使用 Lastfm Api 与 酷狗歌词Api。项目架构采用 mvp-clean,基于 Retrofit2 Dagger2 Rxjava RxBus Glide。
  • 功能模块: 我的歌曲、我的歌单、文件夹、我喜欢、最近播放、最近添加、播放排行、本地搜索等。
  • 支持显示歌词及缓存
  • 支持耳机线控播放,耳机拔出自动暂停
  • 动态刷新媒体库,及时获知媒体文件变更
  • 日夜间模式切换,支持动态换肤

于是我跑到美区app store下了tidal,注册账号,主页就是大大的宣传。但是听不了。

                                                                                                                          :)

目录

实现的功能

  • 播放暂停(点击切换播放状态)
  • 下一曲(切换下一首)
  • 随机播放(当前歌曲播放完自动播放下一曲)
  • 单曲循环(点击随机播放图标可切换成单曲循环)
  • 音量调节(鼠标移入滑动设置音量大小)
  • 歌曲进度条(可点击切换进度直接跳,也可以点击小圆点拖拽切换进度)
  • 实时歌词(点击词,切换歌词界面,根据实时进度自动滚动歌词)
  • 喜欢(点击添加了一个active效果)
  • 分享(可以直接分享到新浪微博)
感谢原作者及作者联系方式:
  • Github: github.com/hefuyicoder
  • Email: hefuyicoder@gmail.com

www.55402.com 10喜欢就点个赞.gif

我又到淘宝去搜,太早了,卖家都没起床啊亲,后来就已经看到有资源了。

《那不是我》:

  • 02.音视频播放 QQ音乐界面搭建 (自己整理)
  • QQ音乐界面搭建
    • 1.storyboard布局QQ音乐界面
      • QQ音乐主界面整体框图
      • 界面控件设置
    • 2.实现音乐的播放
      • 封装WXAudioTool播放音效和播放音乐工具类
      • 播放音效/音乐工具类实现步骤
      • 封装WXMusicTool获取音乐列表工具类
      • 在主控制器实现播放音乐
    • 3.添加播放进度定时器,更新播放进度
      • 实现更新播放进度功能和歌手图标旋转功能
      • 实时更新播放进度信息
      • 歌手图标旋转动画
    • 4.处理滚动条
      • 自定义滚动歌词的WXLrcScrollView,继承UIScrollView
    • 5.播放/暂停,上一首,下一首功能实现,音乐播放完毕自动切换到下一首
      • 播放/暂停,上一首,下一首功能实现
      • 音乐播放完毕自动切换到下一首
    • 6.设置进度条UISlider的处理
      • 监听进度条的事件
      • 进度条添加Tap敲击手势
    • 7.歌词的解析
      • 歌词模型类功能的实现
      • 创建歌词文件处理WXLrcTool工具类
    • 8.实现歌词ScrollView歌词的滚动功能
      • 主界面控制器创建更新歌词的定时器CADisplayLink定时器
      • 自定义显示歌词的WXLrcLabel
      • 实现歌词的滚动,当前行歌词进度颜色填充效果
    • 10.设置主界面的歌词
      • 在主界面中设置主界面歌词
      • 由lrcScrollView内部为主界面歌词内容和进度进行更新
    • 11.实现锁屏界面信息展示和操作
      • 锁屏界面项目配置
    • 12.实现锁屏锁屏歌词展示
      • 绘制锁屏封面和歌词
      • 锁屏界面实现播放,暂停,上一首,下一首功能

audio 标签使用

  • autoplay 自动播放
  • loop 循环播放
  • volume 音量设置
  • currentTime 当前播放位置
  • duration 音频的长度
  • pause 暂停
  • play 播放
  • ended 返回音频是否已结束

但是这个时候我被tidal的界面给惊到了,竟然有这美的音乐播放器啊,有种黑胶唱片封套的感觉,可以翻开,里边介绍更多歌曲的信息,包括词曲作者、制作人等信息。给幕后制作人员足够的尊重,赞了。

http://music.163.com/#/m/song?id=352031&userid=55784380


播放和暂停代码

_Music.prototype.playMusic = function(){
    var _this = this;
    this.play.on('click', function(){
        if (_this.audio.paused) {
            _this.audio.play();
            $(this).html('');
        } else {
            _this.audio.pause();
            $(this).html('')
        }
    });
}

于是就充值了,三个月先。

《火车司机男朋友》:

QQ音乐界面搭建

  • QQ音乐运行效果(模拟器不能演示锁屏界面的功能,所以展示效果图没有锁屏界面功能展示)

www.55402.com 11

1.QQ音乐运行效果.gif


音乐进度条代码

_Music.prototype.volumeDrag = function(){
    var _this = this;
    this.btn.on('mousedown', function(){
        _this.clicking = true;
        _this.audio.pause();
    })
    this.btn.on('mouseup', function(){
        _this.clicking = false;
        _this.audio.play();
    })
    this.progress.on('mousemove click', function(e){
        if(_this.clicking || e.type === 'click'){
            var len = $(this).width(),
                left = e.pageX - $(this).offset().left,
                volume = left / len;
            if(volume <= 1 || volume >= 0){
                _this.audio.currentTime =  volume * _this.audio.duration;
                _this.progressLine.css('width', volume *100  '%');
            }
        }
    });
}

还有一点啊,为何欧美那边听歌不用滚动歌词呢?明明超级好用。

http://music.163.com/#/m/songid=352047&userid=55784380

1.storyboard布局QQ音乐界面

歌词添加代码

_Music.prototype.readyLyric = function(lyric){
    this.lyricBox.empty();
    var lyricLength = 0;
    var html = '<div class="lyric-ani" data-height="20">';
    lyric.forEach(function(element,index) {
        var ele = element[1] === undefined ? '^_^歌词错误^_^' :  element[1];
        html  = '<p class="lyric-line" data-id="' index '" data-time="'   element[0]   '"> '    ele   ' </p>';
        lyricLength  ;
    });
    html  = '</div>';
    this.lyricBox.append(html);
    this.onTimeUpdate(lyricLength);
}

代码还有很多就不一一添加了,觉得还行的话可以点下喜欢(也可以在我的GitHub给个Star),你的喜欢和Star是我继续创作的动力,非常感谢!!!

有什么疑问或问题,欢迎大家指出。

music有歌词但不滚动,tidal根本就不提供歌词,迷。

QQ音乐主界面整体框图

  • 界面效果图

www.55402.com 12

2.QQ音乐主界面整体框图.png

  • UISlider添加手势

www.55402.com 13

3.UISlider添加手势.png

  • UILabel作为其他控件布局的参考控件注意点
    • 如果UILabel的(顶部Top或底部Bottom)作为其他控件布局的参考对象的时候,需对设置UILabel的高度约束. 高度约束如下图所示

www.55402.com 14

4.高度约束.png

www.55402.com 15

界面控件设置

  • 设置毛玻璃效果的几种方式
    • 美工做一张毛玻璃效果的图片
    • 使用UIToolbar
    • 使用第三方框架
    • 使用coreImage
    • 使用UIVisualEffectView (Blur)

www.55402.com 16

5.使用UIVisualEffectView (Blur).png

  • 设置背景毛玻璃效果
    • 本示例使用UIToolbar做毛玻璃效果
    • 示例代码
/** 给背景添加毛玻璃效果 */
- (void)setupBlur
{
    // REMARKS: 添加毛玻璃效果
    // 1.创建toolBar,设置毛玻璃样式,添加到背景的view
    UIToolbar *toolBar = [[UIToolbar alloc] init];
    toolBar.barStyle = UIBarStyleBlack;
    [self.albumView addSubview:toolBar];

    // 2.toolBar添加约束
    [toolBar mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.albumView);
    }];
}
  • 设置歌手图标圆角效果
    • view即将布局子控件的时候调用viewWillLayoutSubviews方法,该方法中可以获取子控件的真实尺寸
// SINGLE: view即将布局子控件的时候调用,在该方法中可以拿到子控件的真实尺寸
- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];

    // SINGLE: 设置ImageView为圆形
    self.iconView.layer.cornerRadius = self.iconView.frame.size.width * 0.5;
    self.iconView.layer.masksToBounds = YES;
    // SINGLE: 设置ImageView边框颜色,宽度
    self.iconView.layer.borderColor = [UIColor lightGrayColor].CGColor;
    self.iconView.layer.borderWidth = 5.0;
}
  • audio标签自制音乐播放器,丹麦乐团。设置UISlider滚动条滑块图片
// SINGLE: 2.设置UISlider滑块图片
[self.progressSlider setThumbImage:[UIImage imageNamed:@"player_slider_playback_thumb"] forState:UIControlStateNormal];

www.55402.com 17

2.实现音乐的播放

www.55402.com 18

封装WXAudioTool播放音效和播放音乐工具类

www.55402.com 19

播放音效/音乐工具类实现步骤

  • 1.创建一个可变字典用于存放音乐播放器AVAudioPlayer,创建一个可变字典用于存放播放音效SystemSoundID,作为音乐播放器和音效播放的内存缓存.
    • 音乐播放器和音效播放器缓存只需创建一次,所以将其放在initialize方法中进行初始化操作.示例代码如下
#pragma mark - 初始化设置
/** 创建内存缓存 */
  (void)initialize {
// 创建可变字典,用于存放播放音效SystemSoundID
_soundIDs = [NSMutableDictionary dictionary];

// 创建可变字典,用于存放音乐播放器AVAudioPlayer
_players = [NSMutableDictionary dictionary];
}
  • 2.WXAudioTool工具类实现播放等API接口方法

    • 实现播放音乐,暂停音乐,停止音乐和播放音效API接口方法

    播放音乐: (AVAudioPlayer *)playMusicWithMusicName:(NSString *)musicName;
    暂停音乐: (void)pauseMusicWithMusicName:(NSString *)musicName;
    停止音乐: (void)stopMusicWithMusicName:(NSString *audio标签自制音乐播放器,丹麦乐团。)musicName;
    播放音效: (void)playSoundWithSoundName:(NSString *)soundName;

  • 3.WXAudioTool工具类实现代码

#import "WXAudioTool.h"
#import <AVFoundation/AVFoundation.h>

@implementation WXAudioTool

// SINGLE: 创建一个可变字典缓存音乐,字典只需创建一次,可以在initialize类方法中创建
static NSMutableDictionary *_soundIDs;
// SINGLE: 创建音乐内存缓存,在initialize类方法中创建
static NSMutableDictionary *_players;

#pragma mark - 初始化设置
/** 创建内存缓存 */
  (void)initialize {
    // 创建可变字典,用于存放播放音效SystemSoundID
    _soundIDs = [NSMutableDictionary dictionary];

    // 创建可变字典,用于存放音乐播放器AVAudioPlayer
    _players = [NSMutableDictionary dictionary];
}


#pragma mark - 播放音乐API(AVAudioPlayer)
/** 播放音乐 */
  (AVAudioPlayer *)playMusicWithMusicName:(NSString *)musicName
{
    // 1.先从内存字典中获取播放器AVAudioPlayer
    AVAudioPlayer *player = _players[musicName];

    // 2.判断是否从内存获取到播放器,如果没有获取到,新建播放器
    if (player == nil) {
        // 2.1 获取音乐文件的url
        NSURL *url = [[NSBundle mainBundle] URLForResource:musicName withExtension:nil];
        if (url == nil) return nil;

        // 2.2 根据音频文件的url,创建播放器
        player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];

        // 2.3 保存到内存缓存_players
        [_players setObject:player forKey:musicName];
    }

    // SINGLE: 3.播放音乐
    [player play];

    return player;
}

/** 暂停音乐 */
  (void)pauseMusicWithMusicName:(NSString *)musicName
{
    // 1.从内存字典中取出播放器
    AVAudioPlayer *player = _players[musicName];

    // 2.如果内存中有获取到播放器,暂停播放
    if (player) {
        // SINGLE: 暂停播放
        [player pause];
    }
}

/** 停止音乐 */
  (void)stopMusicWithMusicName:(NSString *)musicName
{
    // 1.从内存字典中取出播放器
    AVAudioPlayer *player = _players[musicName];

    // 2.如果内存中有获取到播放器,停止播放
    if (player) {
        // SINGLE: 2.1 停止播放
        [player stop];
        // 2.2 从内存字典中移除
        [_players removeObjectForKey:musicName];
        player = nil;
    }
}


#pragma mark - 播放短音效API(SystemSoundID)

// REMARKS: 播放音效类方法
/** 播放音效 */
  (void)playSoundWithSoundName:(NSString *)soundName
{
    // 1.先从内存缓存获取soundID
    SystemSoundID soundID = [_soundIDs[soundName] unsignedIntValue];

    // 2.判断内存是否存在音效资源,内存没有音效资源,则创建
    if (soundID == 0) {
        // 2.1 若不存在, 创建音效资源url
        CFURLRef url = (__bridge CFURLRef)[[NSBundle mainBundle] URLForResource:soundName withExtension:nil];

        // 2.2 判断url是否为空,如果为空,说明资源不存在,直接退出
        if (url == nil) return;

        // 2.3 生成SystemSoundID
        AudioServicesCreateSystemSoundID(url, &soundID);

        // 2.4 存入可变字典内存缓存
        [_soundIDs setObject:@(soundID) forKey:soundName];
    }

    // 3.播放音效
    AudioServicesPlaySystemSound(soundID);
}

@end

© 本文版权归作者  藤井 樹  所有,任何形式转载请联系作者。

封装WXMusicTool获取音乐列表工具类

  • 1.创建WXMusicItem音乐模型
@interface WXMusicItem : NSObject
/** 音乐名 */
@property (nonatomic ,copy)NSString *name;
/** 音乐文件名 */
@property (nonatomic ,copy)NSString *filename;
/** 歌词文件名 */
@property (nonatomic ,copy)NSString *lrcname;
/** 歌手名 */
@property (nonatomic ,copy)NSString *singer;
/** 歌手小图标 */
@property (nonatomic ,copy)NSString *singerIcon;
/** 歌手大图标 */
@property (nonatomic ,copy)NSString *icon;
@end
  • 2.WXMusicTool工具类实现API接口方法

    • 实现播放音乐,暂停音乐,停止音乐和播放音效API接口方法
      获取所有的音乐: (NSArray *)musics;
      获取正在播放的音乐: (WXMusicItem *)playingMusic;
      设置播放的音乐: (void)setupMusic:(WXMusicItem *)music;
      获取上一首音乐: (WXMusicItem *)previous;
      获取下一首音乐: (WXMusicItem *)next;
  • 3.WXMusicTool工具类实现代码

#import "WXMusicTool.h"
#import "WXMusicItem.h"
#import <MJExtension.h>

@implementation WXMusicTool

#pragma mark - 静态变量
/** 所有音乐 */
static NSArray *_musicItems;
/** 当前播放的音乐 */
static WXMusicItem *_playingMusicItem;

/** 获取所有音乐模型,设置当前默认音乐 */
  (void)initialize
{
    // 从plist文件中获取所有音乐模型
    _musicItems = [WXMusicItem mj_objectArrayWithFilename:@"Musics.plist"];
    // 设置当前默认音乐
    _playingMusicItem = _musicItems[4];
}

#pragma mark - 播放音乐操作
/** 获取所有的音乐 */
  (NSArray *)musics
{
    return _musicItems;
}

/** 获取正在播放的音乐(默认) */
  (WXMusicItem *)playingMusic
{
    return _playingMusicItem;
}

/** 设置播放的音乐 */
  (void)setupMusic:(WXMusicItem *)music
{
    _playingMusicItem = music;
}

/** 获取上一首音乐 */
  (WXMusicItem *)previous
{
    // 1.获取当前音乐的下标值
    NSInteger currentIndex = [_musicItems indexOfObject:_playingMusicItem];

    // 2.获取上一首音乐的下标值,判断是否越界
    NSInteger previousIndex = currentIndex - 1;
    if (previousIndex < 0) {
        previousIndex = _musicItems.count - 1;
    }

    // 3.获取上一首的音乐
    WXMusicItem *previousMusicItem = _musicItems[previousIndex];

    // 4.返回上一首音乐
    return previousMusicItem;
}

/** 获取下一首音乐 */
  (WXMusicItem *)next
{
    // 1.获取当前音乐的下标值
    NSInteger currentIndex = [_musicItems indexOfObject:_playingMusicItem];

    // 2.获取下一首音乐的下标值,判断是否越界
    NSInteger nextIndex = currentIndex   1;
    if (nextIndex >= _musicItems.count) {
        nextIndex = 0;
    }

    // 3.获取下一首的音乐
    WXMusicItem *nextMusicItem = _musicItems[nextIndex];

    // 4.返回下一首音乐
    return nextMusicItem;
}
@end

在主控制器实现播放音乐

  • 播放音乐实现
#pragma mark - 播放音乐
- (void)playingMusic
{
    // 1.获取当前音乐
    WXMusicItem *playerMusicItem = [WXMusicTool playingMusic];

    // 2.更新子控件信息
    self.albumView.image = [UIImage imageNamed:playerMusicItem.icon];
    self.iconView.image = [UIImage imageNamed:playerMusicItem.icon];
    self.songLabel.text = playerMusicItem.name;
    self.singerLabel.text = playerMusicItem.singer;

    // 3.开始播放音乐
    AVAudioPlayer *currentPlayer = [WXAudioTool playMusicWithMusicName:playerMusicItem.filename];
    // 3.0 设置代理,用来监听音乐播放完毕,实现自动切换到下一首的功能
    currentPlayer.delegate = self;
    self.currentPlayer = currentPlayer;
    // 3.1 设置当前播放时间和音乐总时长
    self.currentLabel.text = [NSString stringWithTime:currentPlayer.currentTime];
    self.totalLabel.text = [NSString stringWithTime:currentPlayer.duration];
    // 3.2 更新当前播放按钮的状态
    self.playOrPauseBtn.selected = self.currentPlayer.isPlaying;
    // 3.3 设置当前播放的音乐的歌词
    self.lrcScrollView.lrcFileName = playerMusicItem.lrcname;
    // 3.4 将当前播放的音乐的总时长传给lrcScrollView,用于做锁屏界面的总时长
    self.lrcScrollView.duration = currentPlayer.duration;

    // 4.添加旋转动画
    [self addIconViewAnimate];

    // 5.添加定时器(需先移除定时器在添加,避免当前定时器还在运行,又开启新定时器)
    [self removeProgressTimer];
    [self addProgressTimer];

    // 6.添加更新歌词定时器
    [self removeLrcTimer];
    [self addLrcTimer];

    // 7.设置默认当前音乐播放时间,总时长和进度条
    [self updateProgressInfo];
}

3.添加播放进度定时器,更新播放进度

1.添加定时器步骤,需先移除当前定时器,再添加定时器.
2.将定时器添加到NSRunLoop,并设置NSRunLoopCommonModes模式.
3.歌手图标旋转动画实现,使用基础核心动画CABasicAnimation,设置绕z轴旋转360°无限循环等动画属性配置.

实现更新播放进度功能和歌手图标旋转功能

  • 播放进度定时器创建/移除方法
    • 创建播放进度定时器: - (void)addProgressTimer;
    • 移除播放进度定时器: - (void)removeProgressTimer;

实时更新播放进度信息

  • 更新进度进度实现
- (void)updateProgressInfo {
    // 1.更新当前音乐播放时间和当前音乐总时长
    self.currentLabel.text = [NSString stringWithTime:self.currentPlayer.currentTime];
    self.totalLabel.text = [NSString stringWithTime:self.currentPlayer.duration];

    // 2.更新进度条信息
    self.progressSlider.value = self.currentPlayer.currentTime / self.currentPlayer.duration;
}

歌手图标旋转动画

  • 歌手图标旋转动画实现
- (void)addIconViewAnimate
{
    // 1.创建核心动画,并设置期相关属性
    CABasicAnimation *anim = [CABasicAnimation animation];
    // 1.1 设置绕z轴旋转360°,无限循环等
    anim.keyPath = @"transform.rotation.z";
    anim.fromValue = @(0);
    anim.toValue = @(M_PI * 2);
    anim.repeatCount = CGFLOAT_MAX;
    anim.duration = 35;
    // SINGLE: 1.2 当进入后台,再进入前台时,核心动画会失效,需设置removedOnCompletion属性为NO,这样核心动画就不会失效
    // removedOnCompletion: 设置为NO表示动画完成的时候不要移除.
    anim.removedOnCompletion = NO;

    // 2.添加动画到self.iconView.layer
    [self.iconView.layer addAnimation:anim forKey:nil];
}

4.处理滚动条

自定义滚动歌词的WXLrcScrollView,继承UIScrollView

  • 1.在WXLrcScrollView初始化时,添加滚动歌词的tableView
    • initWithCoderinitWithFrame方法中开启分页功能,初始化和添加tableView到WXLrcScrollView
    • 设置子控件tableView的数据源和代理,实现数据源方法,为tableView提供显示的测试数据
    • 在layoutSubviews中添加tableView的约束,并设置tableView背景颜色,分割线,内边距等,必须在layoutSubviews中设置,否则会tableView中的歌词播放不同步,背景为白色等问题.
/** layoutSubviews中布局子控件tableView */
- (void)layoutSubviews
{
    [super layoutSubviews];
    // 1.布局tableView
    [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.mas_top);
        make.bottom.equalTo(self.mas_bottom);
        make.height.equalTo(self.mas_height);
        make.left.equalTo(self.mas_left).offset(self.bounds.size.width);
        make.right.equalTo(self.mas_right);
        make.width.equalTo(self.mas_width);
    }];

    // SINGLE: 2.清空tableView背景颜色,取消tableView的分割线,设置tableView的内边距
    self.tableView.backgroundColor = [UIColor clearColor];
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    self.tableView.contentInset = UIEdgeInsetsMake(self.tableView.bounds.size.height * 0.5, 0, self.tableView.bounds.size.height * 0.5, 0);
}

5.播放/暂停,上一首,下一首功能实现,音乐播放完毕自动切换到下一首

在storyboard中设置播放按钮在Normal/Selected状态下的按钮显示的图片

播放/暂停,上一首,下一首功能实现

  • 播放/暂停功能实现

    • 切换播放按钮的状态
    • 判断是否正在播放,如果当前正在播放则暂停播放,反之则继续播放
      • 暂停播放: 暂停播放,移除定时器,暂停动画(分类实现暂停和继续动画)
      • 继续播放: 继续播放,开启定时器,继续动画
  • 上一首/下一首功能实现

    • 使用WXMusicTool工具类获取上一首/下一首音乐
    • 实现切换音乐的方法
  • 示例代码

/** 下一首 */
- (IBAction)nextMusic {

    // 1.获取下一首音乐
    WXMusicItem *nextMusicItem = [WXMusicTool next];

    // 2.播放下一首音乐
    [self playMusic:nextMusicItem];

}
/** 上一首 */
- (IBAction)previousMusic {
    // 1.获取上一首音乐
    WXMusicItem *previousMusicItem = [WXMusicTool previous];

    // 2.播放上一首音乐
    [self playMusic:previousMusicItem];
}
/** 切换播放的音乐 */
- (void)playMusic:(WXMusicItem *)music {
    // 1.获取当前音乐,并暂停播放
    WXMusicItem *curMusicItem = [WXMusicTool playingMusic];
    [WXAudioTool pauseMusicWithMusicName:curMusicItem.filename];

    // 2.设置当前播放音乐music
    [WXMusicTool setupMusic:music];

    // 3.开始播放音乐
    [self playingMusic];
}

音乐播放完毕自动切换到下一首

  • 主控制器的当前播放器遵守AVAudioPlayerDelegate代理协议
  • 在播放器创建完成时设置代理,用来监听音乐播放完毕,实现自动切换到下一首的功能
    self.currentPlayer.delegate = self;
  • 实现代理方法audioPlayerDidFinishPlaying:successfully,当前音乐正常播放完成后调用.
#pragma mark - <AVAudioPlayerDelegate>代理协议
// SINGLE: 当前音乐播放完成后调用,在<AVAudioPlayerDelegate>代理协议中
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
    // flag == YES 表示音乐播放正常停止,播放完毕自动切换到下一首
    if (flag) {
        // 音乐播放完毕自动切换下一首
        [self nextMusic];
    }
}

6.设置进度条UISlider的处理

监听进度条的事件

  • 监听进度条TouchDown事件,当UISlider监听到TouchDown事件时,移除进度条定时器
  • 监听进度条TouchUpInside事件,当UISlider监听到TouchUpInside事件时,更新当前播放进度,并重新开启定时器
  • 监听到进度条ValueChange事件,拖动UISlider时,使用UISlider的value属性和当前音乐总时长计算当前播放进度,并更新当前播放时间Label的文字信息.
    self.currentLabel.text = [NSString stringWithTime:self.progressSlider.value * self.currentPlayer.duration];

进度条添加Tap敲击手势

  • 在Main.storyboard给UISlider进度条添加点击Tap手势

www.55402.com 20

6.进度条添加Tap敲击手势.png

  • 监听敲击手势代码实现
/** 监听进度条点击Tap手势 */
- (IBAction)sliderTap:(UITapGestureRecognizer *)sender {
    // 1.获取当前点的位置
    CGPoint curPoint = [sender locationInView:sender.view];

    // 2.获取当前点与总进度的比例
    CGFloat ratio = curPoint.x / self.progressSlider.bounds.size.width;

    // 3.更新当前播放器的播放时间
    self.currentPlayer.currentTime = ratio * self.currentPlayer.duration;

    // 4.更新当前播放时间Label,总时长Label的信息和进度条信息,因为updateProgressInfo方法使用self.currentPlayer.currentTime值,所以必须在第3步执行完才能执行updateProgressInfo方法,如果顺序反之,则不行
    [self updateProgressInfo];
}

/** 实时更新播放进度信息 */
- (void)updateProgressInfo {
    // 1.更新当前音乐播放时间和当前音乐总时长
    self.currentLabel.text = [NSString stringWithTime:self.currentPlayer.currentTime];
    self.totalLabel.text = [NSString stringWithTime:self.currentPlayer.duration];

    // 2.更新进度条信息
    self.progressSlider.value = self.currentPlayer.currentTime / self.currentPlayer.duration;
}

7.歌词的解析

歌词模型类功能的实现

  • 歌词模型类的属性: 当前行歌词的播放时间,歌词内容
  • 实现当前行歌词的解析,当前行歌词数据格式: [00:33.20]只是因为在人群中多看了你一眼
#import "WXLrcLineItem.h"

@implementation WXLrcLineItem

/** 创建WXLrcLineItem的对象方法 */
- (instancetype)initWithLrcLineString:(NSString *)lrcLineString
{
    if (self = [super init]) {
        // 1.解析歌词
        [self lrcStringToItem:lrcLineString];
    }
    return self;
}

/** 创建WXLrcLineItem的类方法 */
  (instancetype)lrcLineItemWithLrcLineString:(NSString *)lrcLineString
{
    return [[self alloc] initWithLrcLineString:lrcLineString];
}

/** 解析歌词 */
- (void)lrcStringToItem:(NSString *)lrcLineString
{
    // 歌词数据: [00:33.20]只是因为在人群中多看了你一眼
    // 1.以"]"切割歌词与时间
    NSArray *lrcArray = [lrcLineString componentsSeparatedByString:@"]"];

    // 2.解析出歌词内容 只是因为在人群中多看了你一眼
    self.name = lrcArray[1];

    // 3.解析时间 [00:31.25
    NSString *timeString = lrcArray[0];
    self.time = [self timeWithTimeString:[timeString substringFromIndex:1]];
}

/** 解析时间 时间数据: 00:31.25 */
- (NSTimeInterval)timeWithTimeString:(NSString *)timeString {

    NSInteger min = [[timeString componentsSeparatedByString:@":"][0] integerValue];
    NSInteger sec = [[timeString substringWithRange:NSMakeRange(3, 2)] integerValue];
    NSInteger mSec = [[timeString componentsSeparatedByString:@"."][1] integerValue];

    return min * 60   sec   mSec * 0.01;
}
@end

创建歌词文件处理WXLrcTool工具类

  • 实现将歌词文件转换成歌词模型数组
/** 传入本地歌词文件名,解析歌词文件 */
  (NSArray *)lrcToolWithLrcFileName:(NSString *)lrcFileName
{
    // 1.获取歌词的路径
    NSString *filePath = [[NSBundle mainBundle] pathForResource:lrcFileName ofType:nil];

    // 2.读取歌词文件数据
    NSString *lrcString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];

    // SINGLE: 3.通过n字符切割到数组
    NSArray *lrcArray = [lrcString componentsSeparatedByString:@"n"];

    /** 
     歌词文件头部信息
     [ti:]
     [ar:]
     [al:]
     */
    // 4.遍历数组,将数组转模型
    NSMutableArray *lrcArrayM = [NSMutableArray array];
    for (NSString *lrcLineString in lrcArray) {
        // 1.过滤歌词文件头部无用信息,不是以[开头的也过滤掉
        if ([lrcLineString hasPrefix:@"[ti:"] || [lrcLineString hasPrefix:@"[ar:"] || [lrcLineString hasPrefix:@"[al:"] || ![lrcLineString hasPrefix:@"["]) {
            continue;
        }

        // 2.解析歌词数据到模型
        WXLrcLineItem *lrcItem = [WXLrcLineItem lrcLineItemWithLrcLineString:lrcLineString];

        // 3.添加到可变数组
        [lrcArrayM addObject:lrcItem];
    }

    return lrcArrayM;
}

8.实现歌词ScrollView歌词的滚动功能

主界面控制器创建更新歌词的定时器CADisplayLink定时器

  • 提供创建/移除用于更新歌词的定时器的方法
  • 在定时器定时执行的方法中将当前的播放时间传递给lrcScrollView的currentTime属性,让lrcScrollView实现歌词的进度更新
    self.lrcScrollView.currentTime = self.currentPlayer.currentTime;

自定义显示歌词的WXLrcLabel

  • 对外提供当前行歌词的进度progress属性,让外部为其设置当前歌词的进度
  • 重写- (void)drawRect:(CGRect)rect方法绘制WXLrcLabel
    • UIRectFill填充Label颜色,不是填充文字颜色. UIRectFill(fullRect);
    • UIRectFillUsingBlendMode填充文字函数,kCGBlendModeSourceOut:表示填充文字以外的区域
- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];

    // 1.设置填充的颜色
    [[UIColor greenColor] set];

    // 2.设置要填充的尺寸,根据传递过来的歌词播放进度
    CGRect fullRect = CGRectMake(0, 0, self.bounds.size.width * self.progress, self.bounds.size.height);

    // 3.开始绘制
    // SINGLE: UIRectFill会填充Label颜色,不是填充文字颜色
//    UIRectFill(fullRect);
    // SINGLE: UIRectFillUsingBlendMode(fullRect, kCGBlendModeSourceIn)填充文字,kCGBlendModeSourceOut:表示填充文字以外的区域
    UIRectFillUsingBlendMode(fullRect, kCGBlendModeSourceIn);
}
  • 重写progress当前行歌词播放的进度,用于刷新重绘Label,在方法内部调用setNeedsDisplay重绘Label

实现歌词的滚动,当前行歌词进度颜色填充效果

  • 重写lrcFileName属性set方法来设置tableView中的歌词
    • 歌词bug注意: 必须将指向当前播放的歌词行数清0,否则在音乐快播放完成时手动切换下一首会导致程序崩溃.奔溃原因: 假设当前音乐歌词总60行,下一首音乐歌词共38行,当前播放到55行是调到下一首,下一首最大才38行,这样会导致tableView的数据源数组访问越界.
- (void)setLrcFileName:(NSString *)lrcFileName
{
    // CARE: 0.切换音乐前,将当前播放的歌词清0,否则会出现当前音乐快播完后,手动切换下一首导致程序奔溃
    // 奔溃原因: 假设当前音乐歌词总60行,下一首音乐歌词共38行,当前播放到55行是调到下一首,下一首最大才38行,这样会导致tableView的数据源数组访问越界
    // 0.指向当前播放的歌词行数清0
    self.currentIndex = 0;

    // 1.保存歌词名
    _lrcFileName = lrcFileName;

    // 2.解析歌词,保存到数组
    self.lrcList = [WXLrcTool lrcToolWithLrcFileName:lrcFileName];
    // CARE: 初始设置歌词的第0行
    WXLrcLineItem *firstItem = self.lrcList[0];
    self.lrcLabel.text = firstItem.name;

    // 3.刷新tableView
    [self.tableView reloadData];
}
  • 实时刷新当前歌词进度

    • 滚动tableView的方法, scrollPosition: UITableViewScrollPositionTop表示tableView滚动到顶部
      *- (void)scrollToRowAtIndexPath:(NSIndexPath )indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated;**

    • 计算当前行歌词的进度
      获取当前行歌词进度 当前行歌词进度 = (当前播放的时间 - 当前行歌词的开始时间) / (下一行歌词的开始时间 - 当前行歌词的开始时间)

    • 因该方法每秒执行60次,考虑到内部刷新列表操作和性能问题,判断如果当前行是正在播放的歌词,就无需刷新,通过self.currentIndex != i,如果不等于i,才进入刷新列表

    • www.55402.com,设置当前行歌词后,要刷新当前行和上一行歌词的cell

/** 重写当前播放时间set方法,该方法每秒会调用60次,因为外部用CADisplayLink定时器刷新歌词进度 */
- (void)setCurrentTime:(NSTimeInterval)currentTime
{
    // 1.保存当前播放时间
    _currentTime = currentTime;

    // 2.获取歌词的总数
    NSInteger count = self.lrcList.count;
    // 3.遍历歌词数组
    for (NSInteger i = 0; i < count; i  ) {
        // 3.1 获取第i位置的歌词模型
        WXLrcLineItem *currentLrcItem = self.lrcList[i];

        // 3.2 获取第i 1位置的歌词的模型
        NSInteger nextIndex = i   1;
        WXLrcLineItem *nextLrcItem = nil;
        if (nextIndex < count) {
            nextLrcItem = self.lrcList[nextIndex];
        }

        // 3.3 判断当前播放时间是否在第 i ~ i 1歌词之间  (i位置的时间 <= self.currentTime < i 1位置的时间)
        // CARE: 因该方法每秒执行60次,考虑到内部刷新列表操作和性能问题,判断如果当前行是正在播放的歌词,就无需刷新,通过self.currentIndex != i,如果不等于i,才进入刷新列表
        if ( (self.currentIndex != i) && (currentTime >= currentLrcItem.time && currentTime < nextLrcItem.time) ) {

            // 1.滚动当前正在播放的歌词到中心位置,实际是滚动到最顶部,因为之前有设置内边距顶部间距是ScrollView的一半
            // SINGLE: 调用哪个tableView的滚动方法
            NSIndexPath *currentIndexPath = [NSIndexPath indexPathForRow:i inSection:0];
            [self.tableView scrollToRowAtIndexPath:currentIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];

            // CARE: 刷新主界面歌词Label内容
            self.lrcLabel.text = currentLrcItem.name;

            // 2.刷新上一行歌词,如果没刷新,会导致上一行的歌词字体样式和当前歌词的字体样式一样
            NSIndexPath *previousIndexPath = [NSIndexPath indexPathForRow:self.currentIndex inSection:0];

            // CARE: 3.记录当前滚动的歌词,下面刷新cell有用到self.currentIndex,此处顺序不能和以下相反
            self.currentIndex = i;

            // 4.刷新当前行和上一行歌词
            [self.tableView reloadRowsAtIndexPaths:@[currentIndexPath, previousIndexPath] withRowAnimation:UITableViewRowAnimationNone];

            // 5.设置重新绘制锁屏封面和歌词,锁屏界面
            [self setupLockImage];
        }

        // 4.获取当前这句歌词,来获得当前播放的进度,传递当前歌词进度给cell中lrcLabel
        if (self.currentIndex == i) {

            // SINGLE: 1.获取当前行歌词进度 当前行歌词进度 = (当前播放的时间 - 当前行歌词的开始时间) / (下一行歌词的开始时间 - 当前行歌词的开始时间)
            CGFloat progress = (currentTime - currentLrcItem.time) / (nextLrcItem.time - currentLrcItem.time);

            // 2.获取当前显示歌词的cell
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
            WXLrcCell *lrcCell = [self.tableView cellForRowAtIndexPath:indexPath];

            // 3.设置歌词进度,传递给当前歌词进度给cell中lrcLabel
            lrcCell.lrcLabel.progress = progress;
            // CARE: 将当前行歌词进度赋值给主界面传过来的歌词Label;
            self.lrcLabel.progress = progress;
        }
    }
}

10.设置主界面的歌词

在主界面中设置主界面歌词

  • 在初始化歌词的ScrollView的setupLrcScrollView方法中初始设置lrcLabel主界面歌词Label
    self.lrcLabel.text = nil;
    // 3.将主界面的歌词的Label传给lrcScrollView的一个属性 - >lrcLabel,让lrcScrollView为其文字属性,歌词进度赋值
    self.lrcScrollView.lrcLabel = self.lrcLabel;
  • 在scrollViewDidScroll方法中,设置self.lrcLabel的透明度,实现拖动时,主界面歌词渐变效果
/** 监听歌词的lrcScrollView的拖动 */
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    // 1.获取当前点
    CGPoint curPoint = scrollView.contentOffset;

    // 2.通过偏移量获取滑动的比例
    CGFloat ratio = (1 - curPoint.x / self.lrcScrollView.bounds.size.width);

    // 3.改变主界面中间歌手图片iconView和主界面单行歌词的透明度
    self.iconView.alpha = ratio;
    self.lrcLabel.alpha = ratio;
}

由lrcScrollView内部为主界面歌词内容和进度进行更新

  • 在WXLrcScrollView的setCurrentTime方法中设置主界面歌词的内容和进度
    • 刷新主界面歌词Label内容
      self.lrcLabel.text = currentLrcItem.name;
    • 将当前行歌词进度赋值给主界面传过来的歌词Label;
      self.lrcLabel.progress = progress;

11.实现锁屏界面信息展示和操作

锁屏界面项目配置

  • 设置项目可播放音视频步骤

    • 配置后台可播放音视频 工程文件->Capabilities -> Background modes ->Audio

      www.55402.com 21

      7.锁屏界面项目配置.png

    • 创建后台播放音视频的会话,并激活会话

// 1.创建会话
AVAudioSession *session = [AVAudioSession sharedInstance];

// 2.设置类别为后台播放 AVAudioSessionCategoryPlayback: 类别为后台播放,该常量字符串在AVAudioSession.h中
[session setCategory:AVAudioSessionCategoryPlayback error:nil];

// 3.激活会话
[session setActive:YES error:nil];

- 在AppDelegate.m的didFinishLaunchingWithOptions方法中**创建后台播放音视频的会话,并激活会话**

```objectivec
  // REMARKS: 项目配置后台可播放音视频
  // SINGLE: 配置后台可播放音视频 工程文件->Capabilities -> Background modes ->Audio
  // CARE: 模拟器上运行时,音乐可后台运行,但是真机运行默认是不能后台播放音视频的,必须在项目中配置以上操作(后台可播放音视频),需创建会话,设置会话类别为后台播放,并激活会话.

  // 为确保程序运行时会执行到以下设置音视频会话(后台播放会话)代码,所以放在didFinishLaunchingWithOptions方法中执行
  // 1.创建会话
  AVAudioSession *session = [AVAudioSession sharedInstance];

  // 2.设置类别为后台播放 AVAudioSessionCategoryPlayback: 类别为后台播放,该常量字符串在AVAudioSession.h中
  [session setCategory:AVAudioSessionCategoryPlayback error:nil];

  // 3.激活会话
  [session setActive:YES error:nil];

  return YES;
  • 设置锁屏界面显示的内容步骤
    • 获取锁屏中心
      MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
    • 设置锁屏中心要展示的信息,通过设置锁屏中心nowPlayingInfo属性设置,该属性是字典
    • 将设置的字典信息赋给nowPlayingInfo属性
      playingInfoCenter.nowPlayingInfo = playingInfoDict;
    • 让应用程序开启远程事件
      [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
// REMARKS: 设置锁屏界面
/** 设置锁屏界面 */
- (void)setupLockScreenInfoWithLockImage:(UIImage *)lockImage
{
    /*
     // 媒体常量
     MPMediaItemPropertyAlbumTitle           // 媒体音乐的标题(或名称)
     MPMediaItemPropertyAlbumTrackCount
     MPMediaItemPropertyAlbumTrackNumber
     MPMediaItemPropertyArtist               // 作者
     MPMediaItemPropertyArtwork              // 封面
     MPMediaItemPropertyComposer             // 音乐剧作曲家的媒体项目
     MPMediaItemPropertyDiscCount            // 光盘在包含媒体项目的专辑的数目
     MPMediaItemPropertyDiscNumber
     MPMediaItemPropertyGenre
     MPMediaItemPropertyPersistentID
     MPMediaItemPropertyPlaybackDuration     // 媒体项目的播放持续时间(当前播放时间)
     MPMediaItemPropertyTitle                // 显示在作者和标题上面
     */

    // REMARKS: 设置锁屏界面,MPNowPlayingInfoCenter锁屏中心类在MediaPlayer框架中,所以需导入MediaPlayer/MediaPlayer.h头文件
    // 1.获取当前正在播放的音乐
    WXMusicItem *playingMusicItem = [WXMusicTool playingMusic];

    // 2.获取锁屏中心
    MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];

    // 3.设置锁屏中心要展示的信息,通过设置锁屏中心nowPlayingInfo属性设置,该属性是字典
    // 创建要可变字典,用来存放要显示在锁屏中心的信息
    NSMutableDictionary *playingInfoDict = [NSMutableDictionary dictionary];
    // 3.1 设置展示的音乐名称
    [playingInfoDict setObject:playingMusicItem.name forKey:MPMediaItemPropertyAlbumTitle];

    // 3.2 设置展示的歌手名
    [playingInfoDict setObject:playingMusicItem.singer forKey:MPMediaItemPropertyArtist];

    // 3.3 设置展示封面
    MPMediaItemArtwork *artWork = [[MPMediaItemArtwork alloc] initWithImage:lockImage];
    [playingInfoDict setObject:artWork forKey:MPMediaItemPropertyArtwork];

    // 3.4 设置音乐播放的总时间
    [playingInfoDict setObject:@(self.duration) forKey:MPMediaItemPropertyPlaybackDuration];

    // 3.5 设置音乐当前播放的时间
    [playingInfoDict setObject:@(self.currentTime) forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];

    // 3.6 将设置的字典信息赋给nowPlayingInfo属性
    playingInfoCenter.nowPlayingInfo = playingInfoDict;

    // SINGLE: 4.让应用程序开启远程事件
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}

12.实现锁屏锁屏歌词展示

绘制锁屏封面和歌词

  • 将锁屏的封面图片和歌词重新绘制,生成新图片,在显示到显示歌手图标的UIImageView上
#pragma mark - 设置锁屏界面和锁屏歌词

/** 绘制锁屏封面和歌词 */
- (void)setupLockImage
{
    // 1.获取当前音乐的模型
    WXMusicItem *currentMusicItem = [WXMusicTool playingMusic];

    // 2.从当前音乐模型取出封面图片
    UIImage *currentImage = [UIImage imageNamed:currentMusicItem.icon];

    // 3.获取当前,上一行,下一行歌词
    // 3.1 获取当前行歌词
    WXLrcLineItem *currentLrcLine = self.lrcList[self.currentIndex];

    // 3.2 获取上一行歌词
    NSInteger previousIndex = self.currentIndex - 1;
    WXLrcLineItem *previousLrcLine = nil;
    if (previousIndex >= 0) {
        previousLrcLine = self.lrcList[previousIndex];
    }

    // 3.3 获取下一行歌词
    NSInteger nextIndex = self.currentIndex   1;
    WXLrcLineItem *nextLrcLine = nil;
    if (nextIndex < self.lrcList.count) {
        nextLrcLine = self.lrcList[nextIndex];
    }

    // 4.绘制图片
    // 4.1 开启和图片尺寸一样的上下文
    UIGraphicsBeginImageContext(currentImage.size);

    // 4.2 绘制图片
    [currentImage drawInRect:CGRectMake(0, 0, currentImage.size.width, currentImage.size.height)];

    // 4.3 将歌词文字绘制上去
    // 设置文字高度
    CGFloat titleH = 32;

    // 4.3.1 绘制上一句歌词和下一句歌词
    // SINGLE: 设置绘制文字居中
    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
    paragraphStyle.alignment = NSTextAlignmentCenter;
    NSDictionary *otherAttr = @{
                                NSFontAttributeName : [UIFont systemFontOfSize:18],
                                NSForegroundColorAttributeName : [UIColor yellowColor],
                                NSParagraphStyleAttributeName : paragraphStyle
                                };
    // 绘制上一句和下一句歌词(绘制到图片底部)
    [previousLrcLine.name drawInRect:CGRectMake(0, currentImage.size.height - titleH * 3, currentImage.size.width, titleH) withAttributes:otherAttr];
    [nextLrcLine.name drawInRect:CGRectMake(0, currentImage.size.height - titleH, currentImage.size.width, titleH) withAttributes:otherAttr];

    // 4.3.2 绘制当前行歌词文字
    NSDictionary *currentAttr = @{
                                  NSFontAttributeName : [UIFont systemFontOfSize:24],
                                  NSForegroundColorAttributeName : [UIColor greenColor],
                                  NSParagraphStyleAttributeName : paragraphStyle
                                  };
    [currentLrcLine.name drawInRect:CGRectMake(0, currentImage.size.height - titleH * 2, currentImage.size.width, titleH) withAttributes:currentAttr];

    // 4.4 生成绘制好的图片
    UIImage *lockImage = UIGraphicsGetImageFromCurrentImageContext();

    // 4.5 关闭图形上下文
    UIGraphicsEndImageContext();

    // 5.将生成的图片添加到锁屏的封面图片上
    [self setupLockScreenInfoWithLockImage:lockImage];
}

锁屏界面实现播放,暂停,上一首,下一首功能

  • 监听远程事件,实现锁屏界面可操作播放/暂停,上一首,下一首
#pragma mark - 监听远程事件,实现锁屏界面可操作播放/暂停,上一首,下一首
// REMARKS: 监听远程事件,监听锁屏界面操作
/** 监听远程事件 */
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
    switch (event.subtype) {
        case UIEventSubtypeRemoteControlPlay:
        case UIEventSubtypeRemoteControlPause:
            [self playOrPause];
            break;

        case UIEventSubtypeRemoteControlPreviousTrack:
            [self previousMusic];
            break;

        case UIEventSubtypeRemoteControlNextTrack:
            [self nextMusic];
            break;

        default:
            break;
    }
}

版权声明:本文由www.55402.com发布于明星八卦,转载请注明出处:audio标签自制音乐播放器,丹麦乐团