一. QQ音乐播放器的简单实现

每个音乐播放器的实现都大致相同,个人认为难点在于歌曲播放与Slider的同步,歌词的解析与播放的同步。这些过程虽然繁琐,但是理解起来并不难。先来看看简单实现结果吧。

QQ音乐播放器简单实现

虽然功能简单,但是还是耗费了我很长时间来整理其中的逻辑关系,接下来我们就来分析一下音乐播放器的简单实现。

二. 主界面的搭建

这个播放器比较简单,只有一个界面,创建CLPlayingViewController,使用stortyboard布局,先来看一下布局

播放器页面布局

界面共分为四部分,其中需要注意的是 中间歌手图片约束的添加 ,为了保证其在不同的屏幕上都为圆形,这里先将1、3、4部分布局约束添加好,然后设置歌手图片距离上面第1部分和下面第3部分歌词分别有一个距离并且居中显示,然后设置图片长宽比为1:1即可,其他部分的约束比较简单,这里不再赘述,添加约束还是需要多练才能掌握。歌手图片的约束如下。

歌手图片Image的约束

背景图片的毛玻璃效果

添加毛玻璃效果的方法有很多种,如果本来就有一张模糊处理过的美景图片那就最好不过了,如果没有的话只能自己添加,可以使用第三方框架 (DRNRealTimeBlur)或者自己通过coreImage实现高斯模糊。以上两种方法功能强大但是比较麻烦,我们这里只是简单的实现图片模糊,可以使用给UIImageView添加UIToolbar来实现

// 1.初始化toolBar
UIToolbar *toolBar = [[UIToolbar alloc] init];
// 2. 设置frame
toolBar.frame = [UIScreen mainScreen].bounds;
// 3. 设置模糊的样式
toolBar.barStyle = UIBarStyleBlack;
// 4. 添加到imageView
[self.albumView addSubview:toolBar];

而iOS8之后storyboard中出现了专门给图片添加模糊效果的控件。

Blur

我们只需将blur添加到imageView上面然后设置blur的样式即可,

blur的样式

需要注意的是:blur需要添加到背景imageView上面和其他View之间,防止模糊效果影响到歌手图片,播放按钮等其他控件。

歌手图片进行圆角处理

这里直接修改imageView的layer进行圆角处理

- (void)viewWillLayoutSubviews
{[super viewWillLayoutSubviews];// .添加圆角self.iconView.layer.cornerRadius = self.iconView.bounds.size.width * 0.5;self.iconView.layer.masksToBounds = YES;// 设置边框颜色宽度self.iconView.layer.borderColor = CLColor(36, 36, 36, 1.0).CGColor;self.iconView.layer.borderWidth = 5;
}

这里需要注意的是虽然我们在storyboard中为歌手图片添加约束,但是当运行到模拟器上时,屏幕大小和storyboard中屏幕大小可能会不同,如果在viewDidLoad中设置圆角,此时拿到的歌手图片的大小还是storyboard中的大小,所以显示在模拟器上就会使圆形计算错误,因此我们在viewWillLayoutSubviews方法中添加圆角设置。

歌手图片的转动动画效果

图片转动用到核心动画利用CABasicAnimation修改图片z轴进行旋转,设置一定时间旋转一圈,重复无数次。

CABasicAnimation *rotateAnimate = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotateAnimate.fromValue = @(0);
rotateAnimate.toValue = @(M_PI * 2);
rotateAnimate.repeatCount = NSIntegerMax;
rotateAnimate.duration = 36;
[self.iconView.layer addAnimation:rotateAnimate forKey:nil];

最后,修改Slider的原点的样式,我们发现在storyboard是没有办法修改Slider原点的图片的,只能修改颜色,所以需要通过代码修改Slider原点的图片

[self.progressSlider setThumbImage:[UIImage imageNamed:@"player_slider_playback_thumb"] forState:UIControlStateNormal];

三. 播放音乐

这里为了方便使用本地音乐进行播放,首先根据plist文件创建CLMusicModel模型,然后创建CLMusicTool工具类,用来获取所有音乐以及当前正在播放的音乐设置默认播放的音乐等等。最后创建CLAVdioTool工具类用来播放音乐,以及切换上一首,下一首音乐。

接下来来详细分析这三个类的作用。

CLMusicModel模型类仅仅是歌曲模型,内含歌曲名,文件名,歌词文件名,歌手信息等。

CLMusicTool工具类提供方法用来初始化音乐列表将plist文件转化为Model,并存储到数组中,获取所有音乐数组,以及设置默认播放的音乐

static NSArray *_musics;
static CLMusicModel *_playingMusic;
// 类加载的时候初始化音乐列表和播放音乐
+(void)initialize
{if (_musics == nil) {// 使用MJExtension将plist文件转化为模型_musics = [CLMusicModel objectArrayWithFilename:@"Musics.plist"];}if (_playingMusic == nil) {// 设置一开始就播放的音乐_playingMusic = _musics[0];}
}
// 获取所有音乐
+(NSArray *)Musics
{return _musics;
}
// 当前正在播放的音乐
+(CLMusicModel *)playingMusic
{return _playingMusic;
}
// 设置默认播放的音乐
+(void)setUpPlayingMusic:(CLMusicModel *)playingMusic
{_playingMusic = playingMusic;
}

CLAVdioTool工具类用来播放音乐,以及切换上一首,下一首音乐。这里提供三个方法,根据参数文件名找到文件路径并根据文件路径创建播放器player,创建全局字典用来存储播放器,每首歌对应一个播放器,播放音乐的时候先去字典中找到对应的播放器进行播放,如果没有就创建对应的播放器。

static NSMutableDictionary *_players;
+(void)initialize
{_players = [NSMutableDictionary dictionary];
}
+(AVAudioPlayer *)playingMusicWithMusicFileName:(NSString *)filename
{AVAudioPlayer *player = nil;player = _players[filename];if (player == nil) {// 文件路径转化为urlNSURL *url = [[NSBundle mainBundle]URLForResource:filename withExtension:nil];if (url == nil) {return nil;}// 创建playerplayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];// 准备播放[player prepareToPlay];// 将播放器存储到字典中[_players setObject:player forKey:filename];}// 开始播放[player play];return player;
}
+(void)pauseMusicWithMusicFileName:(NSString *)filename
{AVAudioPlayer *player = _players[filename];if (player) {[player pause];}
}
+(void)stopMusicWithMusicFileName:(NSString *)filename
{AVAudioPlayer *player = _players[filename];if (player) {[player stop];[_players removeObjectForKey:filename];player = nil;}
}

此时在CLPlayingViewController中进行音乐播放就非常简单了,使用CLMusicTool获得当前正在播放的CLMusicModel音乐模型,对页面信息进行设置,使用CLAVdioTool根据CLMusicModel的属性音乐名,播放音乐。主要代码如下。

// 获取当前正在播放的音乐
CLMusicModel *playingMusic = [CLMusicTool playingMusic];// 根据文件名播放音乐并且获取播放的音乐
AVAudioPlayer *currentPlayer = [CLAVdioTool playingMusicWithMusicFileName:playingMusic.filename];

四. Slider时间条的处理

播放时间和歌曲总时间的string处理,通过播放器可以拿到已经播放时间currentTime和歌曲总时间duration,播放器返回给我们的是秒,需要将秒转化为分钟,这里给NSString添加分类,添加stringWithTime方法将返回的NSTimeInterval转化为NSString。

+ (NSString *)stringWithTime:(NSTimeInterval)time
{NSInteger min = time / 60;NSInteger sec = (int)round(time) % 60;return [NSString stringWithFormat:@"%02ld:%02ld",min,sec];
}

Slider随播放时间而移动

通过添加定时器的方法,使Slider原点随着播放的时间而移动,将定时器添加到主RunLoop中并修改Mode为NSRunLoopCommonModes防止在滑动时定时器失效。

#pragma mark - 对进度条时间的处理
- (void)addProgressTimer
{// 定时器每1s执行一次,第一次来到这里需要先等1s,所以先刷新一下界面[self updateProgressInfo];self.progressTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateProgressInfo) userInfo:nil repeats:YES];[[NSRunLoop mainRunLoop] addTimer:self.progressTimer  forMode:NSRunLoopCommonModes];
}
- (void)removeProgressTimer
{[self.progressTimer invalidate];self.progressTimer = nil;
}
#pragma mark - 更新进度条
- (void)updateProgressInfo
{// 1.更新播放的时间,使用NSString分类方法处理时间。self.currentTimeLabel.text = [NSString stringWithTime:self.currentPlayer.currentTime];// 2.更新滑动条self.progressSlider.value = self.currentPlayer.currentTime / self.currentPlayer.duration;
}

注意:当我们在播放音乐方法里面添加定时器的时候需要先移除定时器,然后在添加,避免当点击下一首的时候,定时器没有移除,时间发生错误。

Slider滑动更新界面和音乐播放时间

给Slider添加点击事件,监听Slider的滑动。在storyboard中给Slider添加点击事件,分别监听Slider的点击,滑动和松开。

  1. 当按Slider滑块下时移除定时器。
  2. 当滑动Slider滑块时,根据滑动的数值 * 歌曲总时间计算出当前滑动点对应的播放时间,然后更新播放时间label的text。
  3. 当手指松开时,设置播放器播放时间并且添加定时器。
#pragma mark - slider 事件处理
- (IBAction)start {// 移除定时器[self removeProgressTimer];
}
- (IBAction)end {   // 1.更新播放的时间self.currentPlayer.currentTime = self.progressSlider.value * self.currentPlayer.duration;// 2.添加定时器[self addProgressTimer];
}
- (IBAction)progressValueChange {self.currentTimeLabel.text = [NSString stringWithTime:self.progressSlider.value * self.currentPlayer.duration];
}

Slider的点击直接跳转当前时间播放

通过storyboard给Slider添加手势监听Slider的点击,当点击Slider直接跳转到点击位置开始播放。

获取点击的位置,然后计算点击位置占真个Slider的比例,根据比例计算出当前播放时间,最后更新label时间和滑块的位置。

- (IBAction)sliderClick:(UITapGestureRecognizer *)sender {// 1.获取点击到的点CGPoint point = [sender locationInView:sender.view];// 2.获取点击的比例CGFloat ratio = point.x / self.progressSlider.bounds.size.width;// 3.更新播放的时间self.currentPlayer.currentTime = self.currentPlayer.duration * ratio;// 4.更新时间和滑块的位置[self updateProgressInfo];
}

五. 播放暂停、上一首、下一首的点击处理

监听播放按钮点击

播放按钮有播放和暂停两个状态,程序一开始运行就自动播放,所以首先需要在音乐一开始播放的时候修改播放按钮的selected。

self.playWithPauseBtn.selected = currentPlayer.isPlaying;

当点击播放按钮的时候首先需要修改按钮的状态,然后判断音乐播放的状态,如果正在播放则暂停音乐,移除定时器,并且停止歌手图片的动画,如果是暂停的则开始播放,添加定时器,并且回复动画。

暂停动画和恢复动画通过给CALayer添加分类方法实现。分类可以直接拖到别的项目中使用

- (void)pauseAnimate
{CFTimeInterval pausedTime = [self convertTime:CACurrentMediaTime() fromLayer:nil];self.speed = 0.0;self.timeOffset = pausedTime;
}
- (void)resumeAnimate
{CFTimeInterval pausedTime = [self timeOffset];self.speed = 1.0;self.timeOffset = 0.0;self.beginTime = 0.0;CFTimeInterval timeSincePause = [self convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;self.beginTime = timeSincePause;
}

播放按钮点击事件实现

- (IBAction)playWithPause:(UIButton *)sender {sender.selected = !sender.selected;if (self.currentPlayer.playing) {// 1.暂停播放器[self.currentPlayer pause];// 2.移除定时器[self removeProgressTimer];// 3.暂停旋转动画[self.iconView.layer pauseAnimate];} else {// 1.开始播放[self.currentPlayer play];// 2.添加定时器[self addProgressTimer];// 3.恢复动画[self.iconView.layer resumeAnimate];}
}

上一首下一首按钮点击实现

我们可以在CLMusicTool工具类中添加获取上一首歌曲和下一首歌曲的方法,首先拿到当前播放音乐的下标,然后在获取上一首或者下一首歌曲时需要对下标进行判断,拿上一首为例,如果当前歌曲的下标为0,则返回最后一首歌,形成循环播放,如果不为0则获取上一首即可,否则会造成数组越界。获取下一首判断方法相同。

// 返回上一首音乐
+ (CLMusicModel *)previousMusic
{NSInteger index = [_musics indexOfObject:_playingMusic];if (index == 0) {index = _musics.count -1;}else{index = index -1;}CLMusicModel *previousMusic = _musics[index];return previousMusic;
}
// 返回下一首音乐
+ (CLMusicModel *)nextMusic
{NSInteger index = [_musics indexOfObject:_playingMusic];if (index == _musics.count - 1) {index = 0;}else{index = index +1;}CLMusicModel *previousMusic = _musics[index];return previousMusic;
}

此时点击上一首或者下一首按钮时,首先停止当前播放的音乐,然后将上一首或者下一首歌曲设置为默认播放歌曲,最后开始播放,因为停止播放当前音乐,开始播放下一首音乐的代码相同,将其抽成一个方法

- (IBAction)nextMusic {CLMusicModel *nsxtMusic = [CLMusicTool nextMusic];[self playMusicWithMusic:nsxtMusic];
}
- (IBAction)previousMusic {CLMusicModel *previousMusic = [CLMusicTool previousMusic];[self playMusicWithMusic:previousMusic];
}
- (void)playMusicWithMusic:(CLMusicModel *)muisc
{// 获取当前播放的音乐并停止CLMusicModel *playingMusic = [CLMusicTool playingMusic];[CLAVdioTool stopMusicWithMusicFileName:playingMusic.filename];// 设置下一首或者上一首为默认播放音乐[CLMusicTool setUpPlayingMusic:muisc];// 更新界面[self startPlayingMusic];
}

六. 歌词的处理

创建存放歌词的tableView

当滑动歌手图片时,会来到歌词界面,这里往歌手图片和歌词label上面覆盖scrollView,设置scrollView的contentSize为两个屏幕的宽度,显示歌词的tableView放在屏幕外面,布局如图。

歌词tableView布局

使用storyboard添加scrollView并自定义scrollView为CLLrcView,使用代码添加tableView,在scrollView的initWithFrame方法中创建并初始化tableView, 在layoutSubviews中对tableView进行一些设置。例如设置tableView的背景图片为透明,需要cell之间的线,设置tabaleView的contentInset一开始滑动到屏幕中央。

scrollView滑动歌手图片逐渐消失处理

当向右滑动出现歌词时,歌手图片和歌词label是逐渐消失的,我们通过scrollView的代理监听scrollView的滑动,根据scrollView.contentOffset.x的长度占据整个屏幕的比例设置歌手图片和歌词label的透明度,当完全滑动一个屏幕宽度时,歌手图片和歌词label的透明度为0。

#pragma mark - scrollView代理方法
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{CGPoint offcetPoint = scrollView.contentOffset;CGFloat alpha = 1 - offcetPoint.x / self.view.frame.size.width;self.iconView.alpha = alpha;self.lrcLabel.alpha = alpha;
}

自定义tableView的cell和cell中的label

自定义tableView的cell为CLLrcTableViewCell,对cell进行初始化,对cell的style和背景进行设置,对cell内label的frame和字体等进行设置。

自定义label为CLLrcLabel,便于我们之后对label中的歌词进行一些处理。

歌词的显示处理

歌词显示处理逻辑比较繁琐,这里尽量使代码解耦,便于更清晰的理解其中的逻辑。

首先歌词的显示在自定义的CLLrcView中的tableView中,所以给CLLrcView添加lrcName歌词文件名字属性,用来接收歌词文件的名字,然后重写setLrcName:方法根据歌词名获得歌词并对歌词进行一些处理。先来看一下歌词文件中的歌词样式

歌词内容格式

这里需要将每一句歌词转化为歌词模型,模型中包含时间和歌词,并且时间需要转化为秒。

例如 [01:32.64]宁愿相信我们前世有约 需要转化为time = 96 text = @"宁愿相信我们前世有约"存入到模型中。

首先需要将歌词一行一行分开转化为数组,这里创建CLLrcTool工具类用来将每一行歌词分开,并将每一行存入到数组中,此时数组中存储的歌词样式为 [01:32.64]宁愿相信我们前世有约
然后创建CLLrcLine歌词模型,模型中提供方法将 [01:32.64]宁愿相信我们前世有约 样式的歌词分割出时间和歌词内容并转化为模型。

先来看CLLrcTool的将歌词转化为歌词数组方法

+(NSArray *)lrcToolWithLrcName:(NSString *)lrcName
{// 1. 获取路径NSString *lrcFilePath = [[NSBundle mainBundle]pathForResource:lrcName ofType:nil];// 2. 获取歌词NSString *lrcString = [NSString stringWithContentsOfFile:lrcFilePath encoding:NSUTF8StringEncoding error:nil];// 将歌词转化为数组,会以每个\n为分隔符 将每一行转化为数组中的每个元素NSArray *lrcArr = [lrcString componentsSeparatedByString:@"\n"];NSMutableArray *tempArr = [NSMutableArray array];// 遍历数组,将不需要的去除,并调用模型的方法,将字符串转化为模型for (NSString *lrcLineString in lrcArr) {// 过滤掉不要的字符串,如果是以这些开头 或者不是以[开头的直接退出循环if ([lrcLineString hasPrefix:@"[ti:"] ||[lrcLineString hasPrefix:@"[ar:"] ||[lrcLineString hasPrefix:@"[al:"] ||![lrcLineString hasPrefix:@"["]) {continue;}// 将字符串转化为模型CLLrcLine *lrcLine = [CLLrcLine LrcLineString:lrcLineString];[tempArr addObject:lrcLine];}return tempArr;
}

模型中将字符串转化为模型的方法

// 返回模型
+ (instancetype)LrcLineString:(NSString *)lrcLineString
{return [[self alloc] initWithLrcLineString:lrcLineString];
}
// 将字符串分割为时间和歌词内容
- (instancetype)initWithLrcLineString:(NSString *)lrcLineString
{if (self = [super init]) {// [01:32.64]宁愿相信我们前世有约NSArray *lrcArray = [lrcLineString componentsSeparatedByString:@"]"];// 设置歌词内容self.text = lrcArray[1];  // 对时间进行处理然后设置时间self.time = [self timeWithString:[lrcArray[0] substringFromIndex:1]];}return self;
}
// 处理时间,将时间转化为秒
- (NSTimeInterval)timeWithString:(NSString *)timeString
{// 01:32.64NSInteger min = [[timeString componentsSeparatedByString:@":"][0] integerValue];NSInteger sec = [[timeString substringWithRange:NSMakeRange(3, 2)] integerValue];NSInteger hs = [[timeString componentsSeparatedByString:@"."][1] integerValue];return min * 60 + sec + hs * 0.01;
}

此时已经得到歌词模型的数组,刷新tableView即可。

但是此时的歌词是固定的,并不会根据播放时间即时的显示当先播放的时间。

歌词的即时显示

如果想即时的按照播放时间显示歌词,则需要拿到歌曲的总时间并且使用定时器不断的获取当前播放的时间,因为歌词的时间需要比较精确,这里使用CADisplayLink定时器

#pragma mark - 歌词定时器
- (void)addLrcTimer
{self.lrcTiemr = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateLrcInfo)];[self.lrcTiemr addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)removeLrcTimer
{[self.lrcTiemr invalidate];self.lrcTiemr = nil;
}
#pragma mark  更新歌词
- (void)updateLrcInfo
{self.lrcScrollView.currentTime = self.currentPlayer.currentTime;
}

为CLLrcView添加currentTime已播放时间和duration歌曲总时间属性,重写setCurrentTime:对currentTime进行一些判断。

  1. 获取当前歌曲歌词数组的行数。
  2. 遍历获得每一行和下一行歌词的时间。
  3. 进行判断,当当前播放的时间大于等于第i行的时间,并且小于第i+1行的时间则表明当前正在唱的是第i行。
  4. 将第i行移动到屏幕中央,然后将第i行记录下来,更新第i行,回到 tableView:tableView cellForRowAtIndexPath: 方法中判断如果是第i行则将lable的字体放大,如果不是则改为原来的值。
  5. 因为修改第i行内容字体大小之前,第i-1行的内容也被修改过,因此在更新第i行时需要同时更新第i-1行。
  6. 每次切换歌曲时,需要将当前行数清空,避免造成数组越界。

歌词的即时渲染

为达到歌词随播放时间即时渲染变换颜色,通过重写CLLrcLabel的drawRect:方法渲染歌词的颜色,并为CLLrcLabel添加progress属性用来记录歌词的播放进度,通过播放进度的变化随时调用CLLrcLabel的setNeedsDisplay刷新CLLrcLabel的渲染长度。

播放进度 = (当前播放的时间 - 正在唱的歌词的开始时间)/ 当前唱的歌词需要的总时间。

主页面歌词的即时显示

将主页面歌词的label同样设置为CLLrcLabel型,为CLLrcView添加lrcLabel属性,lrcLabel是CLLrcLabel类型的,在获得当前播放放的歌词之后,通过lrcLabel属相将歌词内容回传给主页面歌词label即可。

CLLrcView中setCurrentTime:方法

#pragma mark - 当前播放时间set方法
- (void)setCurrentTime:(NSTimeInterval)currentTime
{// 记录当前时间_currentTime = currentTime;// 获取歌词行数NSInteger count = self.lrcList.count;for (int i = 0; i < count; i ++) {// 获取i位置的歌词CLLrcLine *currentLrcLine = self.lrcList[i];// 获取下一句歌词NSInteger nextIndex = i + 1;// 先创建空的歌词模型CLLrcLine *nextLrcLine = nil;// 判断歌词是否存在if (nextIndex < self.lrcList.count) {// 说明存在nextLrcLine = self.lrcList[nextIndex];}// 用播放器的当前的时间和i位置歌词、i+1位置歌词的时间进行比较,如果大于等于i位置的时间并且小于等于i+1歌词的时间,说明应该显示i位置的歌词。// 并且如果正在显示的就是这行歌词则不用重复判断if (self.currentIndex != i && currentTime >= currentLrcLine.time && currentTime < nextLrcLine.time) {// 设置主页上的歌词self.lrcLabel.text = currentLrcLine.text;// 将当前播放的歌词移动到中间NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];// 记录上一句位置,当移动到下一句时,上一句和当前这一句都需要进行更新行NSIndexPath *previousPath = [NSIndexPath indexPathForRow:self.currentIndex inSection:0];// 记录当前播放的下标。下次来到这里,currentIndex指的就是上一句self.currentIndex = i;[self.tableView reloadRowsAtIndexPaths:@[indexPath,previousPath] withRowAnimation:UITableViewRowAnimationNone];}if (self.currentIndex == i) {// 获取播放速度 已经播放的时间 / 播放整句需要的时间CGFloat progress = (currentTime - currentLrcLine.time) / (nextLrcLine.time - currentLrcLine.time);// 获取当前行数的cellNSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];CLLrcTableViewCell *lrccell = [self.tableView cellForRowAtIndexPath:indexPath];// 设置歌词界面歌词的进度lrccell.lrcLabel.progress = progress;// 设置主页面歌词的进度self.lrcLabel.progress = progress;}}
}

CLLrcLabel的setProgress:方法

- (void)setProgress:(CGFloat)progress
{_progress = progress;[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {[super drawRect:rect];[[UIColor greenColor] set];CGRect fillRect = CGRectMake(0, 0, self.bounds.size.width * self.progress, self.bounds.size.height);UIRectFillUsingBlendMode(fillRect, kCGBlendModeSourceIn);
}

七. 播在放线音乐

虽然项目中播放的是本地音乐,但是使用AVFoundation播放在线音乐也非常简单。

// 1.创建音乐资源
NSURL *url = [NSURL URLWithString:@"url"];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url];
// 2.创建播放器
// AVPlayer *player = [AVPlayer playerWithURL:url];
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
[player play];

注意:AVAudioPlayer只能播放本地音乐,AVPlayerItem既能播放本地音乐也能播放在线音乐

八. 总结

至此,QQ音乐播放器已经基本实现,其中还有许多细节没有处理到位,例如歌曲播放完毕之后的处理,进入后台在返回的旋转动画的处理等,另外对于歌词即时显示感觉讲的还不是很清晰,如果有不清楚的地方还请提出来,我们一起探讨分析一下。

iOS-QQ音乐播放器的简单实现相关推荐

  1. QQ音乐播放器-jQuery实现

    QQ音乐播放器 案例展示 案例实现的功能 静态页面的布局 歌曲信息的动态显示 鼠标悬停,功能按钮和文字高亮 歌曲信息的动态显示 歌曲播放 进度条显示和动态移动 纯净模式的模板设置和歌词写入 案例布局 ...

  2. 【QQ音乐】QQ音乐播放器超酷使用技巧逐个揭秘

    [编者按]QQ音乐播放器凭借QQ强大的人气,目前已经成为许多朋友听歌的首选了,它带给我们无与伦比的快乐音乐体验.QQ音乐播放器的功能是如此的丰富,用户不仅仅是简单的在线听歌,还可以观看MV.收听在线广 ...

  3. 【QT的音乐播放器(简单版)】

    基于QT的音乐播放器 前言 一.主体效果 二.主要技术点: 1. mp3的ID3V2格式文件解析:作者.歌手.时长.专辑图片等 1.1 需要工具: 1.2 ID3V2文件格式 1.3 mp3ID3V2 ...

  4. java播放器使用教程_java 实现音乐播放器的简单实例

    java 实现音乐播放器的简单实例 实现效果图: 代码如下 package cn.hncu.games; import java.applet.Applet; import java.applet.A ...

  5. java音乐播放器视频_java 实现音乐播放器的简单实例

    java 实现音乐播放器的简单实例 实现效果图: 代码如下 package cn.hncu.games; import java.applet.Applet; import java.applet.A ...

  6. html5卡拉OK音乐播放器,QQ音乐播放器怎么打开卡拉OK模式

    QQ音乐播放器怎么打开卡拉OK模式 时间:2020-08-05 12:25:56 责任编辑:随便就行 QQ音乐播放器怎么打开卡拉OK模式?QQ音乐播放器是生活中常用的音乐播放器,很多人在使用QQ音乐播 ...

  7. QQ音乐播放器部分笔记

    QQ音乐播放器案例 1. 基本结构分析 三个部分: 头部区域 中间内容区 左边歌曲列表信息 上边按钮工具条和下边的滑动列表[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img- ...

  8. Qt5学习 模仿qq音乐播放器样式(2)——点击动画效果+歌词颜色变换展示

    拖的太久,主要再上一篇文章中,新学习了相关知识,做了右键菜单,点击按钮动画切换窗口和播放时歌词颜色显示当前播放位置. 主要为了实现功能的展示,所以很多文件读取都直接采用了本地文件这种比较low的方式. ...

  9. 基于jQuery仿QQ音乐播放器网页版代码

    基于jQuery仿QQ音乐播放器网页版代码是一款黑色样式风格的网页QQ音乐播放器样式代码.效果图如下: 在线预览    源码下载 实现的代码. html代码: <div class=" ...

  10. 从零玩转jQuery之项目开发(QQ音乐播放器)

    QQ音乐播放器项目 大体效果如下: HTML结构分析: 一.页面布局 1.首先来看下HTML大体结构: <div class="header"></div> ...

最新文章

  1. 2021年大数据Kafka(十二):❤️Kafka配额限速机制❤️
  2. xml文件上传服务器读取不了,本地读取服务器Xml文件及本地读本地的xml
  3. beanutil 批量copy_Apache Commons Beanutils对象属性批量复制(pseudo-singleton)
  4. yii引入php文件,Yii2框架中CSS、JS文件引入要领_PHP开发框架教程
  5. mysql 聚簇索引和非聚簇索引_MySQL学习之——索引
  6. python变量输出到文件_使用函数将多个变量写入文件
  7. STM32使用DMA接收串口数据
  8. oracle中distinct和group by的区别
  9. 设计模式——19.迭代器模式
  10. mysql5.6.27安装_mysql-5.6.27源码安装及错误解决办法
  11. TortoiseGit安装配置详解同时支持github,gitee,gitlab
  12. Go基础编程:工作区
  13. 初中中考计算机考试,中考信息技术考试
  14. 评价微型计算机有哪些主要性能指标,计算机性能指标有哪些
  15. 最常见绩效考核方法有哪些?5种有效方法介绍
  16. 计算机音乐桃源恋歌,GARNiDELiA《桃源恋歌》[FLAC/MP3-320K]
  17. 拉流与推流的区别_RTSP协议交互流程之推流分析
  18. python异常处理机制例题_16.python异常处理
  19. sunxi:[0]全志SoC启动过程
  20. 魔兽修改默认服务器,魔兽怎么设置默认服务器

热门文章

  1. UML结构建模图———复合结构图
  2. 统计(statistic)(二分查找+离散化)
  3. java根据模板导出pdf
  4. 卖服务器销售打广告语句子,朋友圈销售广告语大全,创意广告语最能打动客户的句子!...
  5. 痛苦的挣扎--msp430g2553我恨你!
  6. PHP获取数组第一个元素和最后一个元素
  7. CR渲染器全景图如何渲染颜色通道_CR渲染如何设置渲染AO图和材质通道?
  8. c语言for循环26个英文字母,C语言创建链表并且插入输出26个英文字母
  9. JSP——编写一个简单的JSP页面,显示英文字母表
  10. 超声波加湿器原理与检修