QQMusic: 仿QQ音乐

您所在的位置:网站首页 qq音乐界面视频 QQMusic: 仿QQ音乐

QQMusic: 仿QQ音乐

#QQMusic: 仿QQ音乐| 来源: 网络整理| 查看: 265

QQ音乐 作者: Liwx 邮箱: [email protected]

目录

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.实现锁屏锁屏歌词展示 绘制锁屏封面和歌词 锁屏界面实现播放,暂停,上一首,下一首功能 QQ音乐界面搭建 QQ音乐运行效果(模拟器不能演示锁屏界面的功能,所以展示效果图没有锁屏界面功能展示)

1.QQ音乐运行效果.gif

1.storyboard布局QQ音乐界面 QQ音乐主界面整体框图 界面效果图

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

UISlider添加手势

3.UISlider添加手势.png

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

4.高度约束.png

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

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; } 设置UISlider滚动条滑块图片 // SINGLE: 2.设置UISlider滑块图片 [self.progressSlider setThumbImage:[UIImage imageNamed:@"player_slider_playback_thumb"] forState:UIControlStateNormal]; 2.实现音乐的播放 封装WXAudioTool播放音效和播放音乐工具类 播放音效/音乐工具类实现步骤 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 *)musicName; 播放音效: + (void)playSoundWithSoundName:(NSString *)soundName;

3.WXAudioTool工具类实现代码

#import "WXAudioTool.h" #import @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 @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 = _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 在initWithCoder和initWithFrame方法中开启分页功能,初始化和添加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 - 代理协议 // SINGLE: 当前音乐播放完成后调用,在代理协议中 - (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手势

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,才进入刷新列表

设置当前行歌词后,要刷新当前行和上一行歌词的cell

/** 重写当前播放时间set方法,该方法每秒会调用60次,因为外部用CADisplayLink定时器刷新歌词进度 */ - (void)setCurrentTime:(NSTimeInterval)currentTime { // 1.保存当前播放时间 _currentTime = currentTime; // 2.获取歌词的总数 NSInteger count = self.lrcList.count; // 3.遍历歌词数组 for (NSInteger i = 0; i Background modes ->Audio 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


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3