模仿 iOS 的 Music App 製作周興哲Eric專屬播放電台(包含進階功能)
苦不堪言
這次花了我整整兩天的時間在製作圖片、研究、找資源,透過Xcode要做播放很簡單,但是要做完該有的功能相對的對新手很需要時間和構想…。
這篇其實很難分享,因為有很多東西要說說不太完,當然也附上source code提供大家參考。
這次所提到的進階功能是包含了背景播放、播完後的動作、以及如何投射到AppleTV,同時實現播放暫停、下一首、上一首、自動更新Slider進度,以及重複播放和隨機播放。
當然歌曲庫的部分是從Youtube載下來,然後整體的樣式背景是透過sketch去找的,所以建議大家要做的時候,可以從這些地方找到資源。
畫面配置
可以去sketch中下載,附上網址提供給大家參考,關於使用教學,可能就是把圖片素材要用到的輸出Export到Finder,然後在拉到Xcode當素材用。
注意事項
由於有些功能在模擬器上不會有作用,所以我都是透過實際機器去跑和測試順便聽歌XD。
參考教學
過程中我參考了很多網站…不及備載。
匯入影片到Xcode
新增Album 的struct class
為了是資料庫的部分可以快速新增,不用太辛苦的一個一個建立,主要要記錄專輯名稱、專輯圖片、歌曲名稱
在ViewController Class與元件建立連結
將main.storyboard內的元件與ViewController建立連結(IBOutlet、IBAction),同時將一些會用到的東西都宣告好,如歌曲資料庫存放的陣列和一些圖片素材。
在viewDidLoad撰寫初始值
這邊會看到音量的部分並未存在於main.storyboard中,為了就是之後播放時候可以直接用系統音量來控制,所以這邊是動態新增音量slider到畫面。
同時在音樂資料庫部分建立album物件將每首歌曲都放入,之後播放時就會參考這個陣列的音樂,而且加上shuffle可以將陣列亂數排序,每次開起來就播放的順序就不同。
設定背景播放部分我先跳過,等等會講到setupRemoteTransportControls這個method在做什麼。
直接跳到playSong()
這個Method主要說明就是播放歌曲,也是我覺得很簡單也可以很複雜的地方,因為程式碼有點長,所以這邊用Github的方式呈現。
這個Method首先就是先判斷playIndex這個值是否有大於音樂資料庫的陣列長度,若超出則歸0,然後再呼叫自己Method一次繼續播放。
那如果一直按上一首的按鍵,就會發生-1的狀況,這時就需要利用陣列長度-1獲得最後一首歌的位置。
接著判斷完畢後,我們要將歌曲從資料庫內取出album物件,然後抓到歌曲名稱、專輯名稱、圖片,接著把這些資訊先set到指定的label上。
// 載入歌曲檔案,取得在手機APP中實際位置
let fileUrl = Bundle.main.url(forResource: songName, withExtension: “mp4”)!
//建立播放的項目
let playerItem = AVPlayerItem(url: fileUrl)
//先移除player播放器內現有的Item,這個很重要,不然壞掉的時候他就會一直亂播同首歌
player.removeAllItems()
//放入現在要播放的Item
player.replaceCurrentItem(with: playerItem)
//指定音量,這部分不太有作用,因為會按照使用者系統指定的音量來播放
player.volume = 0.5
播放進度Slider
由於整個播放時,我們有提供slider來顯示播放進度,所以重新播放一首歌時,都要重新歸0,並計算要播放的這首歌實際長度有多少!
// 重置slider和播放軌道
playbackSlider.setValue(Float(0), animated: true)
let targetTime:CMTime = CMTimeMake(value: Int64(0), timescale: 1)
player.seek(to: targetTime)
播放
那就是直接player.play() 就會開始播放,相反的player.pause()就會暫停。
增加監聽功能更新播放進度slider
這部分我們需要提供給監聽器完整的時間大小,好讓知道如何控制Slider。
// 更新slider時間value
let duration : CMTime = playerItem.asset.duration
let seconds : Float64 = CMTimeGetSeconds(duration)
playbackSlider.minimumValue = 0
playbackSlider.maximumValue = Float(seconds)
// 事件監聽:進度條
addProgressObserver(playerItem:playerItem)
變更UIButton圖案
// 設定播放按鈕圖案
controlButton.setImage(pauseIcon, for: UIControl.State.normal)
讓背景、鎖定時也能夠顯示歌曲資訊
這部分等等會講到這個Method。
// 設定背景當前播放資訊
setupNowPlaying()
播放、暫停鍵IBAction
透過圖片來判斷或者player.rate來判斷歌曲是否正在播放中,如果rate==0代表暫停中,若rate==1則代表歌曲播放中。
事件監聽、更新進度條
這裡大概是針對player的部分增加監聽,監聽頻率為每秒,所以這邊會不斷的計算時間進度,建議這邊在做的人,可以print出來,這樣就比較好追蹤時間的變化,然後再將結果setValue到slider,所以播放時slider就會跑。
播放上下首歌
這邊就比較簡單了,因為複雜的功能都寫在playSong(),所以這邊只要負責變更playIndex然後再呼叫playSong()
設定背景&鎖定播放
這個功能建議可以參閱Apple開發者說明,因為我也是看了很久,不是很懂,不過大概能改得動,其實就是針對背景和鎖定的播放器增加監聽,如果外面做了動做,也要讓裡面的player做動作。
設定背景播放時的歌曲資訊
這部分常常會不太懂是指什麼,所以先附上圖片給大家做參考。
透過這兩張圖,可以看到我們播放的歌曲正顯示在鎖定的畫面上,這時就是需要依賴以下的程式碼幫我們實現。
拖曳播放進度條時,要變更player播放軌道
這邊就是計算拖曳的幅度,然後計算實際要切換的秒數,然後透過player.seek的方式來變更,同時若使用者是暫停狀態下拖曳,拖曳完畢就會繼續播放。
重複播放、隨機播放功能
這邊介紹是如何在歌曲播放完畢後做處理,我們增加AVplayItemDidPlayToEndTime監聽事件,當播放完畢會進入這個區塊執行相對應的程式,所以我在這邊就是播放完畢就移除了進度條的監聽事件,不然之後繼續播放,會再增加一次事件,就會越來越慢。
判斷segment為重複播放還是隨機播放,這邊判斷若是隨機播放,我就給它在隨機處理一次音樂資料庫,並跑一個while迴圈,阻止下次播放與上次一樣,然而在丟到playSong去處理。
若是重複播放,則就在playSong()做囉,而且我的playIndex不會+1,代表是原來的曲目位置繼續在播放。
感想
這個功能真的花了我大把時間在研究,尤其網路上充斥著Object-C的文章,有的還不能用,所以需要去研究更多別人的作法,不過很有趣,歡迎大家分享與指教。
成果分享
Github