仕事で実装する際にハマったので、復習のためにサンプルプロジェクトを作ってみました。
今回は特に分かりづらかったTabBarController/NavigationControllerを使う場合にフォーカスを当てて、「基本は回転を許すが、特定の画面では回転させたくない」というケースを考えてみます。
下準備
「基本は回転を許す」ので、プロジェクトのDevice Orientationは以下の通り設定します。
①TabBarControllerもNavigationControllerも使わない場合
まずはシンプルな2画面があり、ほげほげVCだけは回転させたくない場合について。
解決法
とても簡単で、回転させたくないViewController内に以下を追加するだけでOKです。
// 画面を回転させるかどうか
override var shouldAutorotate: Bool {
return true
}
// 回転方向の指定
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
ちなみに、今回は拡張性を考慮して↑のように書きましたが、下記のようにシンプルにshouldAutorotate
にfalse
を返すやり方でも同じ結果(=回転させない)を得ることができます。(shouldAutorotate
がtrue
でないと、supportedInterfaceOrientations
プロパティは呼ばれないので書かなくてOK)
override var shouldAutorotate: Bool {
return false
}
②TabBarControllerを使う場合
次にTabBarControllerをベースにした2画面があり、ほげほげVCだけは回転させたくない場合について。
とりあえず①と同じコードをほげほげVCに書いてビルドしてみましたが、制御が効かずにほげほげVCも回転してしまいました。
supportedInterfaceOrientations
のドキュメントを見てみると、
When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window.
とあります。なるほど、TabBarControllerやNavigationControllerを使っているとルートのViewControllerが呼ばれるから、回転させたくないViewControllerに書くだけではダメだったんですね。
##解決法
UITabBarController+Orientation.swiftというファイルを作り、TabBarControllerを拡張します。
**ポイントはselectedViewController
を使って、選択されたタブのViewControllerを取得し、そこで設定されたshouldAutorotate
とsupportedInterfaceOrientations
プロパティを取ってくること。**回転させるか否かをTabBarController側では判断せずに、各ViewControllerに委譲する形になります。
extension UITabBarController {
open override var shouldAutorotate: Bool {
// 選択されたタブのViewControllerを取得
if let vc = selectedViewController {
return vc.shouldAutorotate
} else {
return true
}
}
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
// 選択されたタブのViewControllerを取得
if let vc = selectedViewController {
return vc.supportedInterfaceOrientations
} else {
return .allButUpsideDown
}
}
}
その上で、回転させたくないViewControllerに①と同じコードを書きます。
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
これで無事に狙った動きをさせることができました🎉
③NavigationControllerを使う場合
最後にNavigationControllerを使った画面があり、ほげほげVCだけ回転させたくない場合を見てみます。
お約束的に①と同じコードをほげほげVCに直接実装してみますが、ルートのNavigationControllerが呼ばれるためにうまくいかず、ほげほげVCも回転してしまいました。
##解決法
そこで今度はNavigationController+Orientation.swiftというファイルを作り、NavigationControllerを拡張します。
**ポイントはviewControllers.last
を使って、NavigationControllerにぶら下がっているViewControllerを取得し、そこで設定されたshouldAutorotate
とsupportedInterfaceOrientations
プロパティを取ってくること。**TabBarControllerのときと原理は同じですね。
extension UINavigationController {
open override var shouldAutorotate: Bool {
// NavigationControllerにぶら下がっているViewControllerを取得
if let vc = viewControllers.last {
return vc.shouldAutorotate
} else {
return true
}
}
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
// NavigationControllerにぶら下がっているViewControllerを取得
if let vc = viewControllers.last {
return vc.supportedInterfaceOrientations
} else {
return .allButUpsideDown
}
}
}
その上で、回転を制御したいほげほげVCに①と同じコードを追加します。
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
これで無事に狙った動きを得ることができました🎉ここまで来るとだいぶ回転制御がわかってきた気がします。
TabBarController/NavigationControllerの両方を使う場合も書きたかったのですが、疲れてきたのでそれはまた次回にします