はじめに
MPVolumeViewのRouteButtonって何かというと、下の画像のAirPlayボタンのことです。MediaPlayerフレームワークを使うことで、アプリ内にAirPlayボタンを配置できます。
AirPlayボタンは近くにAirPlay可能なデバイスが居る場合に自動で表示されて、タップすると接続機器を選べます。選択すると接続されます。
また、接続可能なデバイスが近くに居ない場合は非表示になります。
音楽プレイヤー、もしくはスピーカーなどの機器と連携するアプリでも作らない限り直面することはないですが、今回作る事になり色々検証していた所、AirPlayボタンが下記の条件で消えてしまう事がわかりました。
1. MPVolumeViewを配置したアプリを起動する前に、コントロールセンターからAirPlayで機器に接続する。
2. 上記アプリを起動する。
3. アプリ上で接続状態を解除する。
4. MPVolumeViewのRouteButtonが消える。
※ちなみにアプリ起動後に接続した場合は、解除しても消えないです。
一旦接続を解除しても機器が使える状態で存在しているのですから、接続するためのボタンが消えてしまうのは困ります。
また、少なくともiOS9.3.5 ~ 10.2.1はこの仕様のようです。
はじめは設定値をいじったり、ボタン自体のalphaを監視して、消えたら強制的に表示するとかを考えてみたのですが、どれも上手くいきませんでした。
対策:消えるなら、消えても良いようにすればいい。
強引ではあるのですが、AirPlayボタンとは別に、UIButtonで作ったダミーのAirPlayボタンを配置することにしました。
色々やってた結果、AirPlayボタンが消える時はalphaが0.0になって見えない&タップ出来ないだけで、ボタンとしては、生きている状態なのがわかっていたので、本家AirPlayボタンは画面外に配置し、下記の様にダミーボタンを押したら、MPVolumeViewの中にあるボタンを押したことにします。
1 2 3 4 5 6 7 8 9 10 |
@IBAction func dummyAirPlayButtonTapped(_ sender: Any) { if let mpVolumeView = self.airplayView { for v in mpVolumeView.subviews { if NSStringFromClass(type(of: v)).contains("Button") { let button: UIButton = v as! UIButton button.sendActions(for: .touchUpInside) } } } } |
ダミーAirPlayボタンはボタンなので、本家AirPlayボタンのように、機器が居る時は表示、居ない時は非表示をする必要があります。
MPVolumeViewのNotificationで、近くに接続可能な機器がある、という情報は受け取れます。
また、今回の仕事では、機器が居なくなったこと検知出来るようになっています。
そのタイミングでダミーボタンを表示したり消したりします。
シンプルにこれらをまとめて書くとこのようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
import UIKit import MediaPlayer class ViewController: UIViewController { var airplayView: MPVolumeView? @IBOutlet weak var dummyAirPlayButton: UIButton! override func viewDidLoad() { super.viewDidLoad() // 本家のAirPlayボタンは画面外に配置 airplayView = MPVolumeView(frame: CGRect(origin: CGPoint(x: -100, y: 0), size: dummyAirPlayButton.bounds.size)) // AirPlayボタンを有効にします airplayView!.showsRouteButton = true // ボリュームのスライダーを無効にします airplayView!.showsVolumeSlider = false self.view.addSubview(airplayView!) // ダミーボタンは非表示にしておく self.dummyAirPlayButton.alpha = 0.0 // AirPlayボタンが有効になったイベントを受け取る let notificationCenter = NotificationCenter.default notificationCenter.addObserver(self, selector: #selector(didWirelessAvailable), name: NSNotification.Name.MPVolumeViewWirelessRoutesAvailableDidChange, object: nil) } // ダミーのAirPlayボタンがタップされたら、画面外のAirPlayボタンをタップしたことにする @IBAction func dummyAirPlayButtonTapped(_ sender: Any) { if let mpVolumeView = self.airplayView { for v in mpVolumeView.subviews { if NSStringFromClass(type(of: v)).contains("Button") { let button: UIButton = v as! UIButton button.sendActions(for: .touchUpInside) } } } } // AirPlayが可能 func didWirelessAvailable() { // もし表示されていなかったら表示する if self.dummyAirPlayButton.alpha == 0.0 { UIView.animate(withDuration: 0.4) { self.dummyAirPlayButton.alpha = 1.0 } } } // 居なくなった func didLostDevice() { UIView.animate(withDuration: 0.4) { self.dummyAirPlayButton.alpha = 0.0 } } } |
これで消えてしまうことなく、AirPlayボタンを利用できるようになります。
最後に
実は知らないだけで消えてしまうことを防げるのかもしませんが、この方法でも見た目AirPlayボタンを使っているように扱えます。