はじめまして、ソリューション技術部の伊藤です。
今携わっているプロジェクトで、iPhone/iPadをHDMIケーブルを使って外部ディスプレイと接続した時に iPhoneと外部ディスプレイで別の画面を表示してほしいという依頼があり、実際に開発した内容をまとめていきたいと思います。
はじめに
通常、iPhone/iPadをAirPlayやHDMI等の有線ケーブルで外部ディスプレイと接続するとミラーリングとなりますが本方法を用いるとiPhone/iPadと接続した外部ディスプレイで別の画面を表示することができます。
例えば、ゲームアプリでiPhoneをコントローラーにし外部ディスプレイをプレイ画面したり、 iPhoneと外部ディスプレイで異なる動画を同時に再生するなどといったことが可能です。
開発の流れ
事前に外部ディスプレイの実装方針や調査結果はもらっていたのですが、 実際に開発するにあたりイメージがつかなかったので自分でも調べました。
ViewController内で外部ディスプレイの接続/切断通知を受け取って画面表示する方法もありますが、外部ディスプレイ側は基本的に同じ画面を表示するため下記のような実装方針となりました。
- SceneDelegateに新たに外部ディスプレイ用のクラスを追加
- 接続があればAppDelegateに通知
- 表示する画面を設定
- AppDelegateのユーティリティを基に画面遷移
実際の実装方法
まずは、アプリ起動したときにiPhoneとは別のViewControllerを表示するようにしていきます。 SceneDelegate、Info.Plistなどに必要な記述、設定を行いました。
SceneDelegate
新しくExternalSceneDelegateを追加します。
SceneDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? // 〜〜〜 省略〜〜〜 } // 外部ディスプレイ用に追加 class ExternalSceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? // 〜〜〜 省略〜〜〜 }
Info.Plist
SceneDelegate>Application Session RoleにExternal configurationを追加します。
AppDelegate
configurationForConnecting で 外部ディスプレイ用画面をセットします。 HDMI接続があればUIApplication.willEnterForegroundNotificationに通知するよう設定しています。
AppDelegate
var window: UIWindow? // 外部ディスプレイ用に追加 var externalWindow: UIWindow? var externalViewController: ExternalViewController? func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { if connectingSceneSession.role == .windowExternalDisplay { let scene = UIWindowScene(session: connectingSceneSession, connectionOptions: options) self.externalWindow = UIWindow(frame: scene.screen.bounds) let rootViewController = R.storyboard.externalDisplay().instantiateInitialViewController() if let controller = (rootViewController as? UINavigationController)?.topViewController as? ExternalViewController { externalViewController = controller } self.externalWindow!.rootViewController = rootViewController self.externalWindow!.windowScene = scene self.externalWindow!.isHidden = false self.externalWindow!.makeKeyAndVisible() // 外部ディスプレイの接続があれば、追加したExternalSceneDelegateを返却 return UISceneConfiguration(name: "External configuration", sessionRole: connectingSceneSession.role) } // それ以外はデフォルトのSceneDelegate return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) }
simulatorで確認
ここまでで、アプリを起動するとiPhoneと外部ディスプレイで別画面が表示されるようになります。 Xcode simulatorで確認するにはウィンドウメニューの I/O>External Displaysで画面サイズを選択すると外部ディスプレイ画面が表示されます。 simulatorで確認すると下記のようになります。
画面遷移してみる
続いて、iPhoneでボタンが押されたら外部ディスプレイ側のみ遷移するようにしていきます。
ViewController
AppDelegate で ユーティリティを定義し、externalViewControllerを参照して遷移します。
AppDelegate
static var shared: AppDelegate { return UIApplication.shared.delegate as! AppDelegate }
ボタンがタップされたら、外部ディスプレイのみ遷移しiPhone側は画面はそのままにします。
ViewController
private func buttonTapped() { let storyboard = UIStoryboard(name: "NextViewController", bundle: nil) let controller = storyboard.instantiateViewController(withIdentifier: "NextViewController") as! NextViewController AppDelegate.shared.externalViewController?.navigationController?.pushViewController(controller, animated: true) }
simulatorで確認
ボタンをタップすると外部ディスプレイ側だけ画面遷移が行われるようになりました。
末筆
外部ディスプレイに対してiOSから任意のコンテンツを表示し、画面遷移までの簡単なまとめとなります。
本記事の実装だけでは遷移だけで画面に動きはないので今後コントローラーとプレイ画面の実装など試してみたいです。
iPhone/iPadと外部ディスプレイで別画面表示させたい時はお試しください。 参考になれば幸いです。
最後までお読みいただきありがとうございました。