你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS 簡單實用的音樂播放器

iOS 簡單實用的音樂播放器

編輯:IOS開發綜合

廢話不多說,老規矩,看成品先:

\

 

尼瑪這東西占得空間太大了,錄不了太多。。。。。。\

 

先介紹吧

 

首先

做個播放器的界面出來,上面是個tableView來加載歌詞,底部兩個Slider,一個聲音,一個進度,最底下三個Button。

\

這裡簡單介紹下用AutoLayout實現底部三個Button等寬,等間距的需求實現

 

// 底部三個按鈕平分屏幕的寬度做法

// 1.首先固定左側第一個按鈕的下和左的約束固定好,其中高度可以給也可以不給,讓文字自動填充

// 2.然後選中三個按鈕,選中垂直對齊以及等寬的兩個必要條件

// 3.之後中間的按鈕只要設置距離左側按鈕的約束就好

// 4.最後讓最右側的按鈕距離右邊的約束,左側的約束固定好,選中三個,按下option + command + =,對齊即可

 

簡單到爆,根本不需要代碼

然後

導入需要操作的歌曲和歌詞進行路徑存儲

\

 

先看看屬性和控件

 

#import "ViewController.h"
#import 
#import "MKJParserLrc.h"
#import "UIImage+ImageEffects.h"
@interface ViewController () 
@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

導入這個頭文件,用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

{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:


這就很好理解了,首先這三首歌的歌詞分別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,千萬別用你喜歡的歌曲去做,不然那首歌就被你毀

了,各位有興趣的去github下載下聽聽,有Bug記得告訴我喲,Love Story確實很好聽,

但是被我聽吐了。。

 

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

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved