iOS 8のデバイスの回転可能な方向の設定

iOSのネイティブアプリケーションを開発するとき、Xcodeのビルドするアプリケーションの設定のDeployment InfoにあるDevice Orientationにあるチェックボックスの設定が重要です。iOS 6以降、この設定がそのままInfo.plistファイルに設定され、アプリケーションはその設定に応じた動きをします。このDevice Orientationは、その方向にデバイスを向けたときに、画面が回転して、画面の上端が実際に上に見えるようになるということを示しています。通常、Upside Downつまり、ホームボタンが上に来る縦長の状態にしたときには、回転は行われないということになっています。つまり、Upside Downの方向にしたときには、写真は90度あるいは180度傾いて見える状態のままになるということです。

shot9897

前の図のDevice Orientationの設定は、iOS 7までは、iPhoneとiPadで別々でした。それぞれで回転可能な方向を決めることができました。iOS 8つまりXcode 6からは、その上のMain InterfaceがiPhoneとiPadで共通になっているので、Device Orientationも共通かと思ったら、Xcode 6.1.1で作ったプロジェクトでは、iPhoneとiPadのDevice Orientationの設定が、別々に定義された状態になっています。以下は、Infoをクリックして見たところで、Supported interface orientationが、iPhone向けの設定であり、General(前の図)で見えている設定です。それに加えて、Supported interface orientation (iPad)もあり、4つの方向がチェックされています。このiPadの設定はGeneralには見えていません。したがって、General側で設定をどう変更しようと、iPadの場合はこのInfoで見えている設定に従うために、すべての方向に回転ができるようになります。

shot9898

ちなみに、iPhoneとiPadの両方の設定が必要なら、Generalのパネルに双方の設定ができるようにすべきです。もしかして、iPadのDevice Orientationの設定は間違えて紛れ込んでいるのでしょうか? ここで、iPad側の設定を削除したいなら、Supported interface orientation (iPad)の項目を選択し、項目名の右に見える、丸にマイナスの部分をクリックします。これで削除できます。

shot9899

ここで、まず、Info.plistに項目が存在するかどうかによって、どのように稼働するのかを見てみます。これを見る限り、既定の動作をさせるには、要するに、これらのInfo.plist項目はそもそもない方が素直な設定の気がします。既定の動作をさせないときに、項目を定義するというのが理にかなっているような気もします。

Supported interface orientation Supported interface orientation (iPad) 回転の動作動作
設定なし 設定なし iPhoneはUpside Down以外が可能、iPadはすべての方向へ可能、つまりデバイス既定の動作
設定あり、要素なし 設定あり、要素なし
設定あり、要素あり 設定なし iPhoneもiPadも、iPhone側の設定に従う
設定あり、要素あり 設定あり、要素あり iPhoneはiPhoneの設定に、iPadはiPadの設定に従う

Supported interface orientationにチェックが入れば、その方向で回転するというのが概略の説明ですが、正しくは1つの場合だけ回転しません。回転しない例が、iPhoneの場合のUpside Downです。つまり、このチェックを入れるだけでは、その方向に回転しないのです。つまり、デバイスの規定値の方が優先順位が高いとみていいでしょう。

では、iPhoneでもUpside Downを可能にするにはどうすればいいか? UIViewControllerにあるsupportedInterfaceOrientationsメソッドにヒントがあり、このメソッドをオーバーライドするのがポイントです。このメソッドは、UIViewControllerであらかじめ定義されており、ビットマスク値を返します。ホームボタンの位置が上下左右のそれぞれに対応する4ビット分のマスク値を返します。UIViewControllerに実装されているメソッドだと、iPadだと4つのビットがすべて1、iPhoneだとUpside Downを除く3つのビットが1になっています。実際に回転していい方向は、Info.plistの設定と、supportedInterfaceOrientationsメソッドの返り値をビットANDをして、残ったビットが回転をしてもいい方向になります。なので、Info.plistの設定で全部にチェックされていても、iPadでは全方向に回転は可能ですが、iPhoneでは、Upside Downはマスク設定で0になっていて、キャンセルされてしまうのです。

それでは、自分で定義しているUIViewControllerの継承クラスで、supportedInterfaceOrientationsメソッドを実装して、全部のビットが1になっている値を返せばいいではないかと考えるところです。しかしながら、この回転の判断は、いちばんルートにあるビューコントローラで判定されます。Single View Applicationで作ったようなプロジェクトだと、いきなり自分で定義しているビューコントローラのクラスがルートにあるので、そこでメソッドを組み込めばいいでしょう。以下のコードに追加されているメソッドを加えます。しかしながら、Master-Detail Applicationのテンプレートで作ったプロジェクトでは、例えば、MasterViewControllerやDetailViewControllerクラスにsupportedInterfaceOrientationsメソッドを作ってもうまくいきません。これらのクラスのオブジェクトはルートのビューコントローラではないからです。Master-Detail Applicationで作ったプロジェクトのストーリーボードファイルを見れば、ルートはUISplitViewControllerクラスのオブジェクトです。そこで、以下のような、UISplitViewControllerクラスを継承したMyRootViewControllerクラスを定義します。そして、メソッドにはsupportedInterfaceOrientationsだけを定義すれば良いでしょう。returnの後のenum型の値が、すべての方向のビット値が1に設定されたものです。返り値がIntなので、rawValueプロパティを使ったり、Int型に変換するなど、Swiftではちょっとややこしいですね。

shot9900

そして、ストーリーボードのUISplitViewControllerクラスのオブジェクトを選択します。右側のユーティリティエリア上部では左から3つ目のアイコンを選択して、アイデンティティインスペクタを表示して、Classのところで、前の図にあるMyRootViewControllerを選択します。

shot9901

こうしておけば、Master-Detail Applicationで作ったプロジェクトでも、iPhoneでUpside Downの方向でも回転ができるようになります。もちろん、プロジェクトのDeployment Infoの設定にあるDevice Orientationのチェックボックスは4つともオンにしておく必要があります。