程序员人生 网站导航

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

栏目:综合技术时间:2016-07-09 13:08:07

    我也不知道为何突然会想写1下音乐播放器的,感觉应当挺好的玩,自己把自己喜欢的歌曲导出来,用程序加载跑

起来,那歌听起来一定很带感啊。。。。。。不过那首Love Story被我听了无数遍。。。。。。听吐了


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


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



尼玛这东西占得空间太大了,录不了太多。。。。。。


先介绍吧


首先

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


这里简单介绍下用AutoLayout实现底部3个Button等宽,等间距的需求实现

// 底部3个按钮平分屏幕的宽度做法

// 1.首先固定左边第1个按钮的下和左的束缚固定好,其中高度可以给也能够不给,让文字自动填充

// 2.然后选中3个按钮,选中垂直对齐和等宽的两个必要条件

// 3.以后中间的按钮只要设置距离左边按钮的束缚就好

// 4.最后让最右边的按钮距离右侧的束缚,左边的束缚固定好,选中3个,按下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; // 当前哪1行 @property (nonatomic,strong) MKJParserLrc *mkj; // 解析歌词用的 @end


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



- (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⑴400x2100.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]]; // 启动定时器,1直更新 [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的进去和歌曲最大时间1致 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

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

- (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:@"]"]; // 3种可能不要,第1种就是头部歌词,第2个时间中没有歌词的,第3个就是没有歌词换行的 if (!([sepArr[0] isEqualToString:@""] || [sepArr[1] isEqualToString:@"\n"] || [sepArr[1] isEqualToString:@"\r\n"])) { [self.timeArr addObject:sepArr[0]]; [self.lrcArr addObject:sepArr[1]]; } } }


第4步

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

// 上1首 - (IBAction)previousSong:(id)sender { [self.audioPlayer stop]; self.mp3Index--; if (_mp3Index==⑴) { 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]; } } // 下1首 - (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 { // 让进度条和当前播放时间1直 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-27 16: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:


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

再切换到上1首或下1首,数组越界了啊,歌词不同,肯定会越界,找到缘由就好办了

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

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


看到这里,1个简单的音乐播放器弄定啦,我心里想你们肯定是这样的


但实际上你们肯定是这样的




血的教训,如果你也要写个Demo,千万别用你喜欢的歌曲去做,不然那首歌就被你毁


了,各位有兴趣的去github下载下听听,有Bug记得告知我哟,Love Story确切很好听,

但是被我听吐了。。


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





不早啦,再听1遍就睡了,各位晚安








------分隔线----------------------------
------分隔线----------------------------

最新技术推荐