プログラミング

【Swift】iPadにおける画面回転を実装する

swift-eyecatch

ユーザーの様々な状態を考慮して、コードを書いていかないといけない、難しい。

画面回転もひとつのハマリポイントで、特にiPadで画面回転に対応する場合は、注意が必要です。

画面回転を検知する方法は、以下の2通りあります。

  • NotificationでUIDevice.orientationDidChangeNotificationを検知する方法
  • ViewControllerでviewWillTransitionをoverrideする方法

2つありますが、Notificationで実装したが、結局iPadでは要件を満たせず、viewWillTransitionで実装したという経緯があります。。

この記事では、Notificatonでの画面検知では対応しきれない要件、その代替策として、viewWillTransitionを実装した理由など、私がハマったポイントとその解決方法をシェアします。

info.plistの設定

画面回転を許容するのであれば、最初にinfo.plistの設定が必要です。

ProjectのTARGETSを選択し、”DeviceOrientation”のすべてにチェックをいれ、”Requires full screen”にチェックが入っていないことを確認します。

“Requires full screen”にチェックが入っていると、「Split View」が使用できなくなります。

Notificationで画面回転を検知する

Notificationで画面回転を検知するには、NotificationにUIDevice.orientationDidChangeNotificationを登録する必要があります。

NotificationCenter.default.addObserver(
    self,
    selector: #selector(orientationDidChange(_:)),
    name: UIDevice.orientationDidChangeNotification,
    object: nil
)

そして、通知を受け取った時に呼ぶ関数を定義して、画面回転後の処理を行う、という流れになります。

@objc func orientationDidChange(_ notification: NSNotification) {
    print("rotated")
}

UIDevice.orientationDidChangeNotificationの困った点

iPadの「Split View」や「Slide Over」の時は使えない

iPhoneのみの対応の場合は、この方法で事足りますが、iPadでの「Split View」や「Slide Over」の時は呼ばれません。

iPadで「Split View」や「Slide Over」を対応するのであれば、必然的にiPadの画面回転も対応する必要があるので、iPadで「画面回転」の時は検知出来るけど「Slide Over」や「Split View」の時は検知しないというのでは困ります。

画面回転後のサイズは取得出来ない

さらに、Notificationではどの画面サイズにtransitionするのかという情報自体は取得出来ないので、通知が飛んできたタイミングでviewのサイズ等を取得する必要があります。

この時、viewのheightを取得するべきなのか、viewのwidthを取得するべきなのか、困ります。

通知が飛んでくるのは画面回転の時だけではない

実装してみるとわかるのですが、このNotificationが呼ばれるのは、画面回転の時だけではありません。

public enum UIDeviceOrientation : Int {

    case unknown //0 

    case portrait //1 //普通の縦向き

    case portraitUpsideDown //2 //逆さ向き

    case landscapeLeft //3 //homeボタンが右

    case landscapeRight //4 //homeボタンが左

    case faceUp //5 //地面に平行向き//スクリーンが上

    case faceDown //6 //地面に平行向き//スクリーンが下
}

このUIDeviceOrientationが変更されたタイミングで通知が飛んでくるので、初めて実装する時はハマります。

例えば、portraitの状態からfaceUp状態になった時に、画面が回転されたと勘違いして、viewのheightやwidthを取得すると、画面の縦横自体は変わっていないのに処理してしまうことでviewが崩れるということもあります。私はハマりました。。

そこで出てくるのが、viewWillTransitionです。

viewWillTransitionで画面回転を検知する

表示されているViewControllerの中で”viewWIllTransition”をoverrideしてその中で画面回転後の処理を記述することで、画面回転に対応することが出来ます。

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    print(size)
}

このviewWillTransitionでは、画面回転後のサイズも受け取ることが出来るので、回転後のサイズも取得することが可能です。

Notificationの場合と違うのは、以下の点です。

  • UIDeviceOrientationが変更されたタイミングで呼ばれるわけではないので、フラット状態を意識する必要がない
  • 画面回転後のサイズを取得することが出来る
  • iPadの「Slide Over」や「Split View」のときの画面サイズ変更時にも呼ばれる。サイズも取得出来る。

実際にアプリケーションを開発するにあたって、フラット状態かどうかなんてほとんど使わないんじゃないかと思います。その時にNotificationが呼ばれるのは逆に不便だなと感じます。

またiPadの画面回転も対応するのであれば、「Split View」も対応必須になるので、viewWillTransitionのほうが使い勝手が良い、というかこっちでないと実装出来ない。

iPad対応をするにあたって、かなりハマってしまいました。。最初からviewWillTransitionで実装すればよかった。。。