目次
- はじめに
- 開発環境
- リピートモードの有効化
- 再生中の曲の状態を取得
- バックグラウンド再生に対応する
- UILabelをオートスクロール(動作に問題あり)
- フルコード
- 完成
- 感想
- 参考資料
はじめに
このアプリを開発する上で、役に立った記事を紹介する。
MPMusicPlayerControllerについては、この記事を参考にして欲しい。
↓Swiftの書式が古いので、注意。
[iOS][Swift]ミュージックライブラリの音楽の再生、情報の表示(MPMusicPlayerController使用)
公式リファレンスも載せておく。
MPMusicPlayerController
ここでは、上記の記事で書かれていない点を書くことにする。
開発環境
- Xcode9.3
- Swift4.1
リピートモードの有効化
var player: MPMusicPlayerController!
//systemMusicControllerにする。
player = MPMusicPlayerController.systemMusicPlayer
//リピートの無効化
player.repeatMode = .none
//リピートの有効化(1曲をリピート)
player.repeatMode = .one
再生中の曲の状態を取得
let playStatus = player.playbackState
//停止中
if playstatus == .stopped {
//処理
}
//一時停止中
else if playstatus == .paused {
//処理
}
//再生中
else if playstatus == .playing {
//処理
}
バックグラウンド再生に対応する
Capabilities
>Background Mode
で、Audio,AirPlay,and Picture in Picture
にチェックを入れる。
僕の環境の場合、これで動いた。
UILabelをオートスクロール(動作不安定)
参考資料:【Swift4.0】テキストのオートスクロールを実装するライブラリ【iOS】
Cocoapodsで、CBAutoScrollLabelをインストール。
pod `AutoScrollLabel`
各種設定はこんな感じ。
artistLabel.labelSpacing = 50; // 開始と終了の間間隔
artistLabel.pauseInterval = 3; // スクロール前の一時停止時間
artistLabel.scrollSpeed = 50.0; // スクロール速度
artistLabel.fadeLength = 20.0; // 左端と右端のフェードの長さ
しかし、実際に導入したものの、稀にスクロールしない時がある。
原因はわからず。。。
フルコード
フルコードを載せておく。
再生ボタンなどは、一から画像を作成して、UIButton.setImageでセットしている。
import UIKit
import MediaPlayer
import AutoScrollLabel
let w = UIScreen.main.bounds.size.width
let h = UIScreen.main.bounds.size.height
class ViewController: UIViewController, MPMediaPickerControllerDelegate {
let artistLabel = CBAutoScrollLabel(frame: CGRect(x: 0, y: w + 160, width: w, height: 30))
let albumLabel = CBAutoScrollLabel(frame: CGRect(x: 0, y: w + 100, width: w, height: 30))
let songLabel = CBAutoScrollLabel(frame: CGRect(x: 0, y: w + 130, width: w, height: 30))
let imageView = UIImageView(frame: CGRect(x: 0, y: 100, width: w, height: w))
var playpause = UIButton(frame: CGRect(x: (w - 60) / 2, y: 580, width: 60, height: 60))
let reb = UIButton(frame: CGRect(x:(w - 60) / 4, y: 580, width: 60, height: 60))
var player: MPMusicPlayerController!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
view.backgroundColor = UIColor.rgb(r: 40, g: 40, b: 40, alpha: 1.0)
player = MPMusicPlayerController.systemMusicPlayer
player.repeatMode = .none
// プレイヤーを止める
player.stop()
// 再生中のItemが変わった時に通知を受け取る
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(type(of: self).b(notification:)), name: NSNotification.Name.MPMusicPlayerControllerPlaybackStateDidChange, object: player)
notificationCenter.addObserver(self, selector: #selector(type(of: self).nowPlayingItemChanged(notification:)), name: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange, object: player)
// 通知の有効化
player.beginGeneratingPlaybackNotifications()
artistLabel.text = ""
artistLabel.font = UIFont.systemFont(ofSize: 15.0)
artistLabel.textAlignment = .center
artistLabel.textColor = .white
artistLabel.labelSpacing = 50;
artistLabel.pauseInterval = 3;
artistLabel.scrollSpeed = 50.0;
artistLabel.fadeLength = 20.0;
view.addSubview(artistLabel)
albumLabel.text = ""
albumLabel.font = UIFont.systemFont(ofSize: 15.0)
albumLabel.textAlignment = .center
albumLabel.textColor = .white
albumLabel.labelSpacing = 50;
albumLabel.pauseInterval = 3;
albumLabel.scrollSpeed = 50.0;
albumLabel.fadeLength = 20.0;
view.addSubview(albumLabel)
songLabel.text = ""
songLabel.font = UIFont.boldSystemFont(ofSize: 30.0)
songLabel.textAlignment = .center
songLabel.textColor = .white
songLabel.labelSpacing = 50;
songLabel.pauseInterval = 3;
songLabel.scrollSpeed = 50.0;
songLabel.fadeLength = 20.0;
view.addSubview(songLabel)
imageView.image = UIImage(named: "noselect.jpg")
view.addSubview(imageView)
//再生・一時停止ボタン配置
view.addSubview(playpause)
let b = UIButton(frame: CGRect(x: w - 60, y: 30, width: 50, height: 50))
b.layer.cornerRadius = 25.0
b.setTitleColor(UIColor.white, for: UIControlState())
b.backgroundColor = UIColor.rgb(r: 255, g: 15, b:115, alpha: 1.0)
b.setTitle("+", for: UIControlState())
b.titleLabel?.font = UIFont.systemFont(ofSize: 28.0)
b.contentHorizontalAlignment = .center
b.addTarget(self, action: #selector(selecter(_:)), for: .touchUpInside)
view.addSubview(b)
let playStatus = player.playbackState
if playStatus == .stopped {
playpause.setImage(UIImage(named :"play.png"), for: UIControlState())
playpause.addTarget(self, action: #selector(play(_:)), for: .touchUpInside)
}
else if playStatus == .paused {
playpause.setImage(UIImage(named :"play.png"), for: UIControlState())
playpause.addTarget(self, action: #selector(play(_:)), for: .touchUpInside)
}
let stop = UIButton(frame: CGRect(x: (w - 60) / 4 * 3, y: 580, width: 60, height: 60))
stop.setImage(UIImage(named :"stop.png"), for: UIControlState())
stop.addTarget(self, action: #selector(Stop(_:)), for: .touchUpInside)
view.addSubview(stop)
reb.setImage(UIImage(named: "repeat.png"), for: UIControlState())
reb.addTarget(self, action: #selector(ChangeRepeat(_:)), for: .touchUpInside)
view.addSubview(reb)
//現在再生中の音楽を表示
let playing = player.nowPlayingItem
player.skipToBeginning()
// 選択した曲から最初の曲の情報を表示
if let mediaItem = playing {
let playstatus = player.playbackState
if playstatus == .stopped {
updateSongInformationUI(mediaItem : mediaItem)
player.stop()
print("stop")
} else if playstatus == .paused {
updateSongInformationUI(mediaItem : mediaItem)
player.stop()
print("pause")
} else {
updateSongInformationUI(mediaItem : mediaItem)
player.stop()
print("playing")
}
} else {
//曲が選択されていない場合、全楽曲セットする
let query = MPMediaQuery.songs()
player.setQueue(with: query)
player.play()
}
}
@objc func selecter(_ :UIButton){
// MPMediaPickerControllerのインスタンスを作成
let picker = MPMediaPickerController()
// ピッカーのデリゲートを設定
picker.delegate = self
// 複数選択にする。(falseにすると、単数選択になる)
picker.allowsPickingMultipleItems = false
// ピッカーを表示する
present(picker, animated: true, completion: nil)
}
//再生
@objc func play(_ :UIButton){
player.play()
playpause.setImage(UIImage(named :"pause.png"), for: UIControlState())
playpause.addTarget(self, action: #selector(Pause(_:)), for: .touchUpInside)
}
//一時停止
@objc func Pause(_ :UIButton){
player.pause()
playpause.setImage(UIImage(named :"play.png"), for: UIControlState())
playpause.addTarget(self, action: #selector(play(_:)), for: .touchUpInside)
}
//停止
@objc func Stop(_ :UIButton){
player.stop()
player.skipToBeginning()
playpause.setImage(UIImage(named :"play.png"), for: UIControlState())
playpause.addTarget(self, action: #selector(play(_:)), for: .touchUpInside)
}
//リピート有効
@objc func ChangeRepeat(_ : UIButton){
player.repeatMode = .one
reb.setImage(UIImage(named: "deleterepeat.png"), for: UIControlState())
reb.addTarget(self, action: #selector(ChangeRepeatStop(_:)), for: .touchUpInside)
}
//リピート無効
@objc func ChangeRepeatStop(_ : UIButton){
player.repeatMode = .none
reb.setImage(UIImage(named: "repeat.png"), for: UIControlState())
reb.addTarget(self, action: #selector(ChangeRepeat(_:)), for: .touchUpInside)
}
// メディアアイテムピッカーでアイテムを選択完了したときに呼び出される
func mediaPicker(_ mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
// プレイヤーを止める
player.stop()
// 選択した曲情報がmediaItemCollectionに入っているので、これをplayerにセット。
player.setQueue(with: mediaItemCollection)
// 選択した曲から最初の曲の情報を表示
if let mediaItem = mediaItemCollection.items.first {
updateSongInformationUI(mediaItem : mediaItem)
}
// ピッカーを閉じる
dismiss(animated: true, completion: nil)
}
// 選択がキャンセルされた場合に呼ばれる
func mediaPickerDidCancel(_ mediaPicker: MPMediaPickerController) {
// ピッカーを閉じる
dismiss(animated: true, completion: nil)
}
// 曲情報を表示する
func updateSongInformationUI(mediaItem: MPMediaItem) {
// 曲情報表示
artistLabel.text = mediaItem.artist ?? "不明なアーティスト"
artistLabel.labelSpacing = 50;
artistLabel.pauseInterval = 3;
artistLabel.scrollSpeed = 50.0;
artistLabel.fadeLength = 20.0;
view.addSubview(artistLabel)
albumLabel.text = mediaItem.albumTitle ?? "不明なアルバム"
albumLabel.labelSpacing = 50;
albumLabel.pauseInterval = 3;
albumLabel.scrollSpeed = 50.0;
albumLabel.fadeLength = 20.0;
view.addSubview(albumLabel)
songLabel.text = mediaItem.title ?? "不明な曲"
songLabel.labelSpacing = 50;
songLabel.pauseInterval = 3;
songLabel.scrollSpeed = 50.0;
songLabel.fadeLength = 20.0;
view.addSubview(songLabel)
// アートワーク表示
if let artwork = mediaItem.artwork {
let image = artwork.image(at: imageView.bounds.size)
imageView.image = image
} else {
// アートワークがないとき
imageView.image = nil
imageView.backgroundColor = .gray
}
player.play()
}
/// 再生中の曲が変更になったときに呼ばれる
@objc func nowPlayingItemChanged(notification: NSNotification) {
if let mediaItem = player.nowPlayingItem {
updateSongInformationUI(mediaItem: mediaItem)
playpause.setImage(UIImage(named :"pause.png"), for: UIControlState())
playpause.addTarget(self, action: #selector(Pause(_:)), for: .touchUpInside)
}
}
//再生中の曲の状態が変わったときに呼ばれる
@objc func b(notification: NSNotification) {
let playStatus = player.playbackState
if playStatus == .stopped {
playpause.setImage(UIImage(named :"play.png"), for: UIControlState())
playpause.addTarget(self, action: #selector(play(_:)), for: .touchUpInside)
}
if playStatus == .paused {
playpause.setImage(UIImage(named :"play.png"), for: UIControlState())
playpause.addTarget(self, action: #selector(play(_:)), for: .touchUpInside)
}
if playStatus == .playing {
playpause.setImage(UIImage(named :"pause.png"), for: UIControlState())
playpause.addTarget(self, action: #selector(Pause(_:)), for: .touchUpInside)
}
}
deinit {
// 再生中アイテム変更に対する監視をはずす
let notificationCenter = NotificationCenter.default
notificationCenter.removeObserver(self, name: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange, object: player)
notificationCenter.removeObserver(self, name: NSNotification.Name.MPMusicPlayerControllerPlaybackStateDidChange, object: player)
// ミュージックプレーヤー通知の無効化
player.endGeneratingPlaybackNotifications()
}
}
//rgbaカラーコード
extension UIColor {
class func rgb(r: Int, g: Int, b: Int, alpha: CGFloat) -> UIColor{
return UIColor(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: alpha)
}
}
完成
感想
結構楽しかったです。
ただ、オートスクロールが稀な確率で動かないのは気になりますね。
現在、調査中です。。
AVAudioPlayerでやる方法もあるので、それを使って、もう少し改造したいですね。