源代碼下載路徑:https://pan.baidu.com/s/1dES8wfR?因?yàn)槔锩娲a中包含的資源過(guò)多,沒(méi)有上傳到我的github賬號(hào)中,直接保存在百度云盤(pán)中。
? ? ? ?這兩天工作上沒(méi)什么事,模仿QQ音樂(lè)播放器寫(xiě)了個(gè)簡(jiǎn)單的QQ音樂(lè)播放界面,可以實(shí)現(xiàn)歌詞同步功能。另外這個(gè)Demo中還設(shè)置了后臺(tái)鎖屏播放界面模式,處于鎖屏狀態(tài)下同樣可以像QQ音樂(lè)那樣自如切換,以及顯示歌詞。
? ? ? ? 看一下震撼的效果圖:
? ? ? ?接下來(lái)這篇文章主要是分析一下本人認(rèn)為有必要說(shuō)明的幾個(gè)點(diǎn)。分別是:滑動(dòng)的時(shí)候顯示歌詞界面問(wèn)題、歌詞按照音樂(lè)播放的進(jìn)度逐漸顯示為綠色的實(shí)現(xiàn)、播放界面imageView旋轉(zhuǎn)和暫停動(dòng)畫(huà)問(wèn)題、鎖屏界面信息配置。可能有人認(rèn)為控制播放界面imageView旋轉(zhuǎn)和暫停動(dòng)畫(huà)是非常簡(jiǎn)單的事情,確實(shí)簡(jiǎn)單,但是有沒(méi)有想過(guò)如何通過(guò)核心動(dòng)畫(huà)去控制動(dòng)畫(huà)的暫停和開(kāi)始。
1.如何在手指向右滑動(dòng)的時(shí)候顯示歌詞界面?
? ? ? 這個(gè)其實(shí)很簡(jiǎn)單,但是如果沒(méi)有做過(guò)類(lèi)似這樣app的開(kāi)發(fā)人員,可能認(rèn)為就是在界面上添加一個(gè)滑動(dòng)的手勢(shì),然后通過(guò)判斷手勢(shì)的滑動(dòng)方向,來(lái)控制歌詞界面的展示和隱藏。這樣做雖然可以實(shí)現(xiàn)同樣的目的,但是會(huì)過(guò)于麻煩。最好的做法是借助UIScrollView控件,直接通過(guò)劃過(guò)scroollView來(lái)展示和隱藏歌詞界面,這種做法干脆利落簡(jiǎn)便。目前很火熱的一些直播類(lèi)app的聊天互動(dòng)界面其實(shí)也都是通過(guò)這樣的方式完成的。在滑動(dòng)scrooView的時(shí)候,我們還可以通過(guò)scroollView的代理方法設(shè)置播放界面的透明度,使界面更加美觀。
2.如何通過(guò)核心動(dòng)畫(huà)控制播放界面imageView旋轉(zhuǎn)和暫停動(dòng)畫(huà)?
? ? ? ?可能以前用核心動(dòng)畫(huà)的時(shí)候一直沒(méi)有涉及到動(dòng)畫(huà)的暫停和開(kāi)始,可是當(dāng)我用核心動(dòng)畫(huà)來(lái)設(shè)置動(dòng)畫(huà)效果,當(dāng)音樂(lè)播放暫停的時(shí)候要停止動(dòng)畫(huà)效果。這時(shí)一下子就懵了,怎樣實(shí)現(xiàn)呢。于是在網(wǎng)上找到了解決方案。正確的做法是創(chuàng)建一個(gè)CALayer的分類(lèi),然后再源文件中實(shí)現(xiàn)以下代碼:
- (void)pauseAnimate
{
? ? ? ? CFTimeInterval pausedTime = [self convertTime:CACurrentMediaTime() fromLayer:nil];
? ? ? ? self.speed = 0.0;
? ? ? ? self.timeOffset = pausedTime;
}
- (void)resumeAnimate
{
? ? ? ? ? ?CFTimeInterval pausedTime = [self timeOffset];
? ? ? ? ?self.speed = 1.0;
? ? ? ? ?self.timeOffset = 0.0;
? ? ? ? ? self.beginTime = 0.0;
? ? ? ? ? ?CFTimeInterval timeSincePause = [self convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
? ? ? ? self.beginTime = timeSincePause;
}
? ? ? ?上面第一個(gè)方法是暫停動(dòng)畫(huà),第二個(gè)方法是開(kāi)始動(dòng)畫(huà)。這個(gè)類(lèi)別可以保存起來(lái),以后碰到其他類(lèi)似問(wèn)題,照舊可以拿來(lái)使用。
3.歌詞如何實(shí)現(xiàn)和播放的進(jìn)度同步?
? ? ? 這里主要講解綠色文字如何和歌詞實(shí)現(xiàn)同步。至于每一句歌詞如何和歌詞同步就簡(jiǎn)單說(shuō)下,主要是通過(guò)定時(shí)器完成,要解析歌詞每一句對(duì)應(yīng)的時(shí)間和文字。綠色文字的同步看起來(lái)課高大上,實(shí)際上很簡(jiǎn)單,代碼量也就簡(jiǎn)短的數(shù)行。首先要知道,每一行歌詞是借助UILabel顯示的,要想事項(xiàng)綠色文字和歌詞同步,我們只需要自定義自己的label類(lèi)即可,但是毋庸置疑這個(gè)自定的這個(gè)label類(lèi)要有一個(gè)進(jìn)度progress屬性。看一下這個(gè)自定義label的源碼。
.h文件實(shí)現(xiàn)
#import@interface ZWLrcLabel : UILabel
/*當(dāng)前播放的進(jìn)度*/
@property(nonatomic,assign)CGFloat progress;
@end
.m文件實(shí)現(xiàn)
#import "ZWLrcLabel.h"
@implementation ZWLrcLabel
- (void)drawRect:(CGRect)rect {
? ? ? ? ?[super drawRect:rect];?
? ? ? ? CGRect fillRect = CGRectMake(0, 0, self.bounds.size.width * self.progress, self.bounds.size.height);
? ? ? [[UIColor greenColor] set];
? ? ? // UIRectFill(fillRect);
? ? ? ?UIRectFillUsingBlendMode(fillRect, kCGBlendModeSourceIn);
}
- (void)setProgress:(CGFloat)progress{
? ? ? ? ?_progress = progress;
? ? ? ? [self setNeedsDisplay];
}
代碼中UIRectFillUsingBlendMode(fillRect, kCGBlendModeSourceIn);是實(shí)現(xiàn)播放進(jìn)度和綠色歌詞文字同步的核心。UIRectFill(fillRect);是label背景色的變化,這行代碼用不到,在這里只是提一下,有興趣的可以看看具體實(shí)現(xiàn)效果。調(diào)用[self setNeedsDisplay];這句代碼就會(huì)調(diào)用- (void)drawRect:(CGRect)rect方法。
4.鎖屏狀態(tài)歌曲播放切換,以及界面顯示是如何自定義的?
首先要想實(shí)現(xiàn)歌詞鎖屏狀態(tài)界面的相關(guān)配置,首先必然要開(kāi)啟后臺(tái)音頻回話。應(yīng)該在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;添加以下三行代碼:
? ? ? ? //1.獲取音頻會(huì)話
? ? ? ?AVAudioSession *session = [AVAudioSession sharedInstance];
? ? ? ? //2.設(shè)置后臺(tái)類(lèi)型
? ? ? [session setCategory:AVAudioSessionCategoryPlayback error:nil];
? ? ? ?//3.激活回話
? ? ? ?[session setActive:YES error:nil];
? ? ? 接下來(lái),先看一下鎖屏界面信息(如圖片,歌曲名,歌手名,時(shí)長(zhǎng)等)配置是如何實(shí)現(xiàn)的,代碼中有注釋。以下代碼在歌曲開(kāi)始播放的時(shí)候,直接調(diào)用即可。
- (void)setupLockScreenInfo{
? ? ? ? ?//0.獲取當(dāng)前播放的歌曲
? ? ? ? ZWMusicModel *playingMusic = [ZWMusicTool playingMusic];
? ? ? ?//1.獲取鎖屏中心
? ? ? ?MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];?
? ? ? ?//2、設(shè)置屏幕參數(shù)
? ? ? ? NSDictionary *playingInfoDict = [NSMutableDictionary dictionary];
? ? ? ?//2.1設(shè)置歌曲名
? ? ? ?[playingInfoDict setValue:playingMusic.name forKey:MPMediaItemPropertyAlbumTitle];
? ? ? //2.2設(shè)置歌手名
? ? ? ?[playingInfoDict setValue:playingMusic.singer forKey:MPMediaItemPropertyArtist];
? ? ? ?//2.3設(shè)置封面圖片?
? ? ? ?MPMediaItemArtwork *artWork = [[MPMediaItemArtwork alloc]initWithImage:[UIImage imageNamed:playingMusic.icon]];
? ? ? ?[playingInfoDict setValue:artWork forKey:MPMediaItemPropertyArtwork];
? ? ? ?//2.4歌曲的總時(shí)長(zhǎng)
? ? ? ?[playingMusic setValue:@(self.currentPlayer.duration) forKey:MPMediaItemPropertyPlaybackDuration];
? ? ? ?playingInfoCenter.nowPlayingInfo = playingInfoDict;
? ? ? ? //3.開(kāi)啟遠(yuǎn)程交互
? ? ? ?[[UIApplication sharedApplication]beginReceivingRemoteControlEvents];
}
最后再來(lái)看看如何實(shí)現(xiàn)后臺(tái)的遠(yuǎn)程交互,控制音樂(lè)的播放暫停,下一曲等。這里要注意兩點(diǎn):a、- (void)remoteControlReceivedWithEvent:(UIEvent *)event是一個(gè)系統(tǒng)方法。b、要想要這個(gè)系統(tǒng)方法生效,在配置鎖屏界面UI相關(guān)信息的時(shí)候,要開(kāi)啟遠(yuǎn)程交互,即[[UIApplication sharedApplication]beginReceivingRemoteControlEvents];
- (void)remoteControlReceivedWithEvent:(UIEvent *)event{
? ? ? ?switch (event.subtype) {
? ? ? ? ? ? ? ? ? case UIEventSubtypeRemoteControlPlay:
? ? ? ? ? ? ? ? ? case UIEventSubtypeRemoteControlPause:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [self playOrPause:self.playButton];//播放或暫停的方法
? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ?case UIEventSubtypeRemoteControlNextTrack:?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [self next:self.nextButton];//下一曲的方法
? ? ? ? ? ? ? ? ?break;
? ? ? ? ? ? ? ? ?case? UIEventSubtypeRemoteControlPreviousTrack:
? ? ? ? ? ? ? ? ? ? ? ? ? [self next:self.previousButton];//上一曲的方法
? ? ? ? ? ? ? ? ?break;
? ? ? ? ? ? ? ? ?default:
? ? ? ? ? ? ? ? ?break;
? ? ? }
}
就簡(jiǎn)單介紹到這里,源代碼中有注釋?zhuān)梢越Y(jié)合這篇文章和源代碼一塊學(xué)習(xí)。