39
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swift4で音楽プレーヤーを作ってみた!(MPMusicPlayerController)

Last updated at Posted at 2018-05-07

目次

  • はじめに
  • 開発環境
  • リピートモードの有効化
  • 再生中の曲の状態を取得
  • バックグラウンド再生に対応する
  • 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にチェックを入れる。
僕の環境の場合、これで動いた。

スクリーンショット 2018-05-07 19.32.22.png

UILabelをオートスクロール(動作不安定)

参考資料:【Swift4.0】テキストのオートスクロールを実装するライブラリ【iOS】

Cocoapodsで、CBAutoScrollLabelをインストール。

pod `AutoScrollLabel`

各種設定はこんな感じ。

artistLabel.labelSpacing = 50; // 開始と終了の間間隔
artistLabel.pauseInterval = 3; // スクロール前の一時停止時間
artistLabel.scrollSpeed = 50.0; // スクロール速度
artistLabel.fadeLength = 20.0; // 左端と右端のフェードの長さ

しかし、実際に導入したものの、稀にスクロールしない時がある
原因はわからず。。。

フルコード

フルコードを載せておく。
再生ボタンなどは、一から画像を作成して、UIButton.setImageでセットしている。

Viewcontroller.swift
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)
    }
}

完成

fullsizeoutput_6ce.jpeg fullsizeoutput_6cc.jpeg

感想

結構楽しかったです。
ただ、オートスクロールが稀な確率で動かないのは気になりますね。
現在、調査中です。。

AVAudioPlayerでやる方法もあるので、それを使って、もう少し改造したいですね。

39
38
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?