簡易音樂播放器(仿網(wǎng)易云音樂)

1.前言

最近用在學(xué)ios的媒體播放,在學(xué)習(xí)之余想找一些項目進(jìn)行實踐。我們都知道網(wǎng)易云音樂是音樂軟件中的良心產(chǎn)品,歌曲推薦功能更是深入人心,可以想象其背后的推薦算法和歌曲分類究竟有多么強大。用AVAudioPlayer寫音樂播放器的時候,自然就想到了模仿網(wǎng)易云音樂,雖然寫得比較簡單,但是在這里還是給大家分享一下。

想直接看代碼略過文章的,點這里github地址。

播放器就長下面這個樣

整體效果.gif

UI設(shè)計確實很丑。(叫程序員來做設(shè)計就是這個結(jié)果) 最上面的sliderView能夠調(diào)節(jié)播放器的音量;下面的UIProgressViewUILabel用來顯示播放器的進(jìn)度;而下一曲和上一曲的按鈕只是為了裝飾中間的播放按鈕。

這個音樂播放器主要分為三個部分

1.UI
2.AVAudioPlayer
3.旋轉(zhuǎn)動畫

  • 因為UI并不是這篇文章的主要內(nèi)容,所以只介紹2和3部分,UI部分我是直接通過(storyboard)來完成的,并且只適合在4-inch的屏幕上運行。(PS:在簡書上怎樣實現(xiàn)頁內(nèi)跳轉(zhuǎn)?markdown中的jump語法好像在簡書中并不是頁內(nèi)跳轉(zhuǎn)。)
  • 我們已經(jīng)添加好了資源,如"music.mp3"和一些素材圖片。

2.AVAudioPlayer

AVAudioPlayer是AVFoundation.framework里面最基本的一個音頻播放器的類。它只能播放一個指定路徑的音頻,特別地方在于它可以控制播放的次數(shù),播放的時間。它還有一個很強大的功能,就是可以很方便的調(diào)節(jié)左右聲道的音量,從而實現(xiàn)很酷的立體聲效果。

首先,引入AVFoundation框架,然后初始化播放器,并加載本地的mp3文件

QQ20160201-0@2x.png
#import <AVFoundation/AVFoundation.h>

property (strong, nonatomic) AVAudioPlayer *player;
//獲取mp3文件的路徑
 NSBundle *bundle = [NSBundle mainBundle];
    NSString * path = [bundle pathForResource:@"music" ofType:@"mp3"];
    NSURL *musicURL = [NSURL fileURLWithPath:path];
    self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:musicURL error:nil];
    //設(shè)置初始的聲音值
    self.player.volume = 2;
    //設(shè)置代理
    self.player.delegate = self;

這樣就創(chuàng)建了一個播放器,要控制播放器AVAudioPlayer中提供了三個方法,分別是:

- (BOOL)play;   //開始播放
- (void)pause;  //暫停播放
- (void)stop;   //停止播放

現(xiàn)在只需要一句[self.player play];,你的模擬器就應(yīng)該有聲音了。

為了讓播放器更符合您的要求,下面列出了AVAudioPlayer的一些屬性

@property(readonly, getter=isPlaying) BOOL playing; //是否正在播放
@property(readonly) NSUInteger numberOfChannels;  //聲道數(shù)
@property(readonly) NSTimeInterval duration;  //播放的總時間
@property NSTimeInterval currentTime;  //當(dāng)前播放時間
@property NSInteger numberOfLoops;  //循環(huán)播放次數(shù)
@property float volume;  //音量大小

3.旋轉(zhuǎn)動畫

選擇我們的播放器已經(jīng)設(shè)置好了UI和播放器,剩下模仿網(wǎng)易云音樂的封面旋轉(zhuǎn)動畫。

在動畫方面我們簡單地使用動畫CABasicAnimation。

CABasicAnimation是layer層提供的基本單一的"keyframe"動畫。我們可以使用animationWithKeyPath:方法來創(chuàng)建一個CABasicAnimation,并且將keyPath存儲在渲染樹中。(類似于cell的Identifier)

代碼如下:

/// 封面圖片
@property (weak, nonatomic) IBOutlet UIImageView *coverImageView;
//創(chuàng)建一個繞z軸選擇的動畫
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    //旋轉(zhuǎn)一周
    animation.toValue = @(2*M_PI);
    //循環(huán)次數(shù),盡量大
    animation.repeatCount = MAXFLOAT;
    //執(zhí)行一次動畫需要的時間
    animation.duration = 50.0f;
    [self.coverImageView.layer addAnimation:animation forKey:@"rotationAnimation"];
  • 需要注意的是,我們一給self.coverImageView.layer添加動畫,這個動畫就馬上開始執(zhí)行了。
  • 至于封面圖片轉(zhuǎn)一圈的時間如何確定,我是看轉(zhuǎn)動的速度來估計的,覺得50秒這個時間效果比較好。(如果有知道確切時間的朋友,留言告訴我一下哈。ヽ(≧Д≦)ノ)

動畫執(zhí)行的效果應(yīng)該是這樣的,封面一直緩慢旋轉(zhuǎn):

封面動畫.gif

4.其它一些代碼

更新播放進(jìn)度條,我們可以設(shè)置一個定時器,每一秒執(zhí)行一次下面的代碼:

/// 播放進(jìn)度
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
/// 播放進(jìn)度的文字Label
@property (weak, nonatomic) IBOutlet UILabel *progressLabel;
//獲取音頻的總時間
NSTimeInterval totalTimer = self.player.duration;
//獲取音頻的當(dāng)前時間
NSTimeInterval currentTime = self.player.currentTime;
    
//根據(jù)時間比設(shè)置進(jìn)度條的進(jìn)度
self.progressView.progress = (currentTime/totalTimer);
    
//把秒轉(zhuǎn)換成分鐘
//當(dāng)前時間的秒和分鐘
int currentM = currentTime/60;
int currentS = (int)currentTime%60;
    
 //總時間的秒和分鐘
int totalM = totalTimer/60;
int totalS = (int)totalTimer%60;
    
//把時間顯示在lable上
NSString *timeString = [NSString stringWithFormat:@"%02d:%02d/%02d:%02d",currentM, currentS, totalM, totalS];
self.progressLabel.text = timeString;

暫停動畫和繼續(xù)動畫

  • 暫停就是將layer的速度設(shè)置為0,將timeOffset設(shè)置為暫停時動畫進(jìn)行的時間。
  • 繼續(xù)動畫就是將layer的速度設(shè)置為1,獲取暫停動畫時設(shè)置的timeOffset和動畫的運行時間,計算并設(shè)置動畫的beginTime。
  • 暫停動畫和繼續(xù)動畫用于用戶點擊了暫停和開始播放按鈕。

暫停動畫:

CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;

繼續(xù)動畫:

CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;

5.最后再嘮叨幾句

這個播放器的內(nèi)容就只有這么多(當(dāng)然不只上面的代碼,還有一些細(xì)節(jié),比如播放按鈕圖片的更換和播放結(jié)束后的處理等等),也有很多不完善的地方。如果有愿意一起改進(jìn)的朋友,歡迎在github地址上找到我哦。

END

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容