我也不知道为什么突然会想写一下音乐播放器的,感觉应该挺好的玩,自己把自己喜欢的歌曲导出来,用程序加载跑起来,那歌听起来必定很带感啊。。。。。。不过那首Love Story被我听了无数遍。。。。。。听吐了

各位看官有兴趣也可以听听。其实前期准备是很坑爹的,找歌词真的蛋疼啊

废话不多说,老规矩,看成品先:

首先

做个播放器的界面出来,上面是个tableView来加载歌词,底部两个Slider,一个声音,一个进度,最底下三个Button。


这里简单介绍下用AutoLayout实现底部三个Button等宽,等间距的需求实现//底部三个按钮平分屏幕的宽度做法// 1.首先固定左侧第一个按钮的下和左的约束固定好,其中高度可以给也可以不给,让文字自动填充// 2.然后选中三个按钮,选中垂直对齐以及等宽的两个必要条件// 3.之后中间的按钮只要设置距离左侧按钮的约束就好// 4.最后让最右侧的按钮距离右边的约束,左侧的约束固定好,选中三个,按下option + command + =,对齐即可

然后

导入需要操作的歌曲和歌词进行路径存储

控件

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "MKJParserLrc.h"
#import "UIImage+ImageEffects.h"
@interface ViewController () <UITableViewDataSource,UITableViewDelegate,AVAudioPlayerDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet UISlider *volSlider;
@property (weak, nonatomic) IBOutlet UISlider *progressSlider;@property (nonatomic,strong) AVAudioPlayer *audioPlayer; // AVAudioPlayer  ---->  音频 本地
@property (nonatomic,strong) NSArray *mp3Arr; // mp3路径
@property (nonatomic,strong) NSArray *lrcArr; // 歌词路径
@property (nonatomic,assign) NSInteger mp3Index; // 当前的播放下表
@property (nonatomic,assign) NSUInteger currentRow; // 当前哪一行@property (nonatomic,strong) MKJParserLrc *mkj; // 解析歌词用的@end

这里我的图片我做了简单的高斯模糊,这里介绍个类给大家,一并把代码都给出来,需要的拿去用把


- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.self.view.backgroundColor = [UIColor colorWithRed:193/255.4 green:193/255.0 blue:193/255.4 alpha:0.7];[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;self.tableView.rowHeight = 60;// 图片高斯模糊UIImage *image = [UIImage imageNamed:@"436c1b64a2b6a4cbab09ee22db3851f4-1400x2100.jpg"];image = [image applyBlurWithRadius:15 tintColor:nil saturationDeltaFactor:1.5 maskImage:nil];self.tableView.backgroundView = [[UIImageView alloc] initWithImage:image];// 存储路径self.mp3Arr = @[[[NSBundle mainBundle] pathForResource:@"Love Story" ofType:@"mp3"],[[NSBundle mainBundle] pathForResource:@"薛之谦-演员" ofType:@"mp3"],[[NSBundle mainBundle] pathForResource:@"华晨宇-异类" ofType:@"mp3"]];self.lrcArr = @[[[NSBundle mainBundle] pathForResource:@"Love Story" ofType:@"lrc"],[[NSBundle mainBundle] pathForResource:@"薛之谦-演员" ofType:@"lrc"],[[NSBundle mainBundle] pathForResource:@"华晨宇-异类" ofType:@"lrc"]];self.mkj = [[MKJParserLrc alloc] init];// 根据路径加载歌曲和歌词[self loadMp3:self.mp3Arr[self.mp3Index] lrcPath:self.lrcArr[self.mp3Index]];// 启动定时器,一直更新[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(changeTime:) userInfo:nil repeats:YES];
}

之后

我们加载MP3歌曲以及解析歌词

#import <AVFoundation/AVFoundation.h>导入这个头文件,用AVAudioPlayer来进行播放
// 加载歌词和歌曲- (void)loadMp3:(NSString *)mp3Str lrcPath:(NSString *)lrcStr
{// 这个方法是获取网上的
//    self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:mp3Str] error:nil];// 下面的是本地的self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:mp3Str] error:nil];self.audioPlayer.delegate = self;self.audioPlayer.volume = 0.5f;// 解析歌词方法[self.mkj parserLrcWithFileURL:lrcStr];// 让slider的进去和歌曲最大时间一致self.progressSlider.maximumValue = self.audioPlayer.duration;// 准备播放[self.audioPlayer prepareToPlay];}

用自己创建的对象进行歌词解析,暴露个方法传本地URL进来

@interface MKJParserLrc : NSObject@property (nonatomic,strong) NSMutableArray *timeArr;
@property (nonatomic,strong) NSMutableArray *lrcArr;- (void)parserLrcWithFileURL:(NSString *)lrcPath;@end

这里分割字符串的方法千千万,咱只是展示一种

- (void)parserLrcWithFileURL:(NSString *)lrcPath
{// 每次进来都清除掉之前的[self.lrcArr removeAllObjects];[self.timeArr removeAllObjects];// 通过路径读取歌词的字符串NSString *lrcStr = [NSString stringWithContentsOfFile:lrcPath encoding:NSUTF8StringEncoding error:nil];// 分割NSArray *lrcArr = [lrcStr componentsSeparatedByString:@"["];// 继续分割for (NSString *sepStr in lrcArr) {// 无脑分割NSArray *sepArr = [sepStr componentsSeparatedByString:@"]"];// 三种可能不要,第一种就是头部歌词,第二个时间中没有歌词的,第三个就是没有歌词换行的if (!([sepArr[0] isEqualToString:@""] || [sepArr[1] isEqualToString:@"\n"] || [sepArr[1] isEqualToString:@"\r\n"])) {[self.timeArr addObject:sepArr[0]];[self.lrcArr addObject:sepArr[1]];}}}


第四步

把点击事件和代理方法实现

// 上一首
- (IBAction)previousSong:(id)sender
{[self.audioPlayer stop];self.mp3Index--;if (_mp3Index==-1) {self.mp3Index = 2;}[self loadMp3:self.mp3Arr[self.mp3Index] lrcPath:self.lrcArr[self.mp3Index]];[self.audioPlayer play];}
// 播放或者暂停
- (IBAction)play:(id)sender {if (self.audioPlayer.playing) {[self.audioPlayer pause];}else{[self.audioPlayer play];}
}
// 下一首
- (IBAction)NextSong:(id)sender
{[self.audioPlayer stop];self.mp3Index++;if (self.mp3Index == 3) {self.mp3Index = 0;}[self loadMp3:self.mp3Arr[self.mp3Index] lrcPath:self.lrcArr[self.mp3Index]];[self.audioPlayer play];
}// 声音change
- (IBAction)volChange:(UISlider *)sender {self.audioPlayer.volume = sender.value;
}
// 进度change
- (IBAction)rateChange:(UISlider *)sender {self.audioPlayer.currentTime = sender.value;
}- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{[self NextSong:nil];
}

最后启动个定时器,让进度条和歌词实时更新,让歌词和歌曲匹配,这个方法也是最关键

// 更新的方法
- (void)changeTime:(NSTimer *)timer
{// 让进度条和当前播放时间一直self.progressSlider.value = self.audioPlayer.currentTime;// 遍历歌词,来记录当前是播放哪个row[self.mkj.timeArr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {NSString *timeStr = self.mkj.timeArr[idx];NSArray *timeArr = [timeStr componentsSeparatedByString:@":"];CGFloat seconds = [timeArr[0] floatValue] * 60 + [timeArr[1] floatValue];if (seconds >= self.audioPlayer.currentTime) {if (idx == 0){self.currentRow = idx;}else{self.currentRow = idx - 1;}*stop = YES;}}];// 刷新[self.tableView reloadData];// 滚动到指定的row现实歌词if (self.currentRow < self.mkj.lrcArr.count) {[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.currentRow inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];}
}

天真的我以为这就完了,这破东西能让你崩的措手不及!!!



 (lldb) po indexPath<NSIndexPath: 0xc000000007a00016> {length = 2, path =0 - 61}2016-06-2716:22:47.557 MusicPlayerDemo[5176:272368] *** Terminating app due to uncaught exception 'NSRangeException', reason:'*** -[__NSArrayM objectAtIndex:]: index 61 beyond bounds [0 .. 51]'*** First throw call stack:

这就很好理解了,首先这三首歌的歌词分别52 46 81行,当我们快速滑动进度条的时候,

再切换到上一首或者下一首,数组越界了啊,歌词不同,肯定会越界,找到原因就好办了

在加载CELL的方法里面加上这个判断就妥妥的了,你想怎么搞都不会蹦了

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];if (indexPath.row < self.mkj.lrcArr.count) {cell.textLabel.text = self.mkj.lrcArr[indexPath.row];}

Demo地址:https://github.com/DeftMKJ/MusicDemo

iOS 简单实用的音乐播放器,少年,自己做个歌单吧。。。。。。相关推荐

  1. linux终端音乐播放器,Linux终端音乐播放器cmus攻略: 操作歌单

    cmus是一款开源的终端音乐播放器.它小巧快速,而又功能强大.cmus支持Ogg/Vorbis.MP3.FLAC.Musepack.WavPack.WMA.WAV.AAC.MP4等格式,包含Gaple ...

  2. 音乐播放器微信小程序开发-歌单界面(简陋版)

    仿QQ音乐app的歌单界面制作了一个简易且简陋的歌单界面. 首先po一张最终效果图(很多功能还没实现,以及还有bug没有改好) 因为不涉及到逻辑,目前只需一些网页开发经验与知识即可(当然啦,还需要一定 ...

  3. 一款简单的本地音乐播放器,界面美观、包括主题切换、歌单管理等等

    MeetMusic 项目地址:lijunyandev/MeetMusic  简介:一款简单的本地音乐播放器,界面美观.包括主题切换.歌单管理等等 音乐-播放器-主题切换- App 效果图        ...

  4. C#制作简单的本地音乐播放器(二)—— 显示歌词

    此篇文章主要补充 C#制作简单的本地音乐播放器(一) 中的"歌词显示"部分的内容. 页面设计部分 相关内容 本程序使用的歌词文件为lrc格式,lrc是英文lyric(歌词)的缩写, ...

  5. iOS开发之网络音乐播放器(SC音乐)(二)

    iOS开发之网络音乐播放器(SC音乐)(二) 前言 iOS开发之网络音乐播放器(SC音乐)(一)已经介绍完播放控制.音乐数据获取解析.歌词显示等.本文在上文的基础上介绍锁屏播放设置,后台播放设置,手势 ...

  6. 用vue简单写一个音乐播放器

    简单地写一个功能比较全的音乐播放器 前言 因为音乐播放器是一个很可能在项目遇到的东西,早写总比晚写好.趁没事先写个. 思路 一个音乐播放器该有的东西: 封面,歌名,专辑,作者 控制器(上一首,下一首, ...

  7. [HTML5]简单网页本地音乐播放器

    既然HTML5提出与本地交互方便,就想写个HTML5的本地音乐播放器.一开始问题主要集中在怎么读取本地文件路径,我想肯定可以用JS实现去操作本地文件(因为node.js很容易实现读取本地文件,但是原生 ...

  8. iOS主题更换、音乐播放器、栏目管理、上传视频封面等源码

    iOS精选源码 主题更换库ThemeStyleTools ios 上传视频选择视频封面(videoCoverController) 网络音乐播放器SCMusic(适配iOS11) OpenGL DEM ...

  9. 给大家分享两款实用的音乐播放器

    01 魔音MORIN 魔音MORIN是一款音乐制作软件,旨在为我们提供高效.专业的音乐制作工具和服务.它具有丰富的音效和特效,支持多种音频格式,包括MP3.WAV.FLAC等.我们可以通过拖拽和点击等 ...

最新文章

  1. GraphSAGE: GCN落地必读论文
  2. ElectronOCR:基于Electron+React+Tesseract的MACOS下的OCR工具
  3. Linux服务器默认建立的LVM逻辑卷磁盘空间分配不合理,根目录不够用,如何缩减和扩展逻辑卷?...
  4. webloigc12服务启动不了_一键启动知多少!
  5. WP8模拟器启动失败解决方法
  6. 深度学习在美团点评推荐平台排序中的应用 widedeep推荐系统模型--学习笔记
  7. 吴恩达深度学习一:神经网络
  8. 关于async和await的探讨
  9. [翻译]欢迎使用C#9.0
  10. python协程--yield和yield from
  11. kibana报错:No default index pattern. You must select or create one to continue.
  12. error:LNK2005 函数已经在*.obj中定义
  13. c语言123 u0010%10,mbsrtowcs_s
  14. [原]初次运用数据缓存机制
  15. Node.js入门(含NVM、NPM、NVM的安装)-(转载)
  16. 汇编语言在线视频教程
  17. 华为星环大数据_大数据平台-华为和星环
  18. 主页 被 2345 篡改怎么办
  19. 【Linux】Linux中755权限是什么意思
  20. 《C++精英内参之程序员高效指南》-12-8影响效率的不良习惯之科学的休息方法

热门文章

  1. Xubuntu22.04设置全局代理(一百五十六)
  2. 机械过滤器(石英砂过滤器)和多介质过滤器的区别
  3. Linux下U盘自动识别和挂载
  4. eclispe中只能输入英文,无法调用输入法
  5. 如何一秒明白请求转发和重定向?
  6. CSS overflow用法
  7. AVA(aesthetic visual analysis)数据集简介
  8. 使用vue2实现打印在线word文件,文件流形式打印在线word,预览打印
  9. finalize方法的使用
  10. MySQL错误:Can't create table‘..’ (errno:150)解决方案