iOS 簡單實用的音樂播放器,少年,自己做個歌單吧。。。。。。
阿新 • • 發佈:2019-02-16
我也不知道為什麼突然會想寫一下音樂播放器的,感覺應該挺好的玩,自己把自己喜歡的歌曲匯出來,用程式載入跑起來,那歌聽起來必定很帶感啊。。。。。。不過那首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];
}