端末を縦に傾けるとUIDevice.orientationDidChangeNotificationが飛んでくる😥

はじめに

iPhoneやiPadで画面回転を考慮したアプリを作成している際、画面回転を検知してレイアウトを変更するためにUIDevice.orientationDidChangeNotification のNotificationでイベントを拾うと思うのですが、UIDevice.orientationDidChangeNotificationには罠があるので注意が必要です。

iPadを左右に回転させた際に呼ばれるのは当然ですが、iPadを縦に傾けた際にも通知が飛んできてしまいます。
※そして重要なのがこの時通知は飛んでくるのですが実際のレイアウトは回転しておらずそのままだということです。

原因

画面回転の状態がどうなっているかはUIDevice.current.orientationで取得することができます。

let orientation = UIDevice.current.orientation

// 指定された方向が上向きか下向きかを示すブール値。
let isFlat = orientation.isFlat
// デバイスが縦向きかどうかを示すブール値。
let isPortrait = orientation.isPortrait
// デバイスが横向きであるかどうかを示すブール値。
let isLandscape = orientation.isLandscape
// 指定された方向が縦向きか横向きかを示すブール値。
let isValidInterfaceOrientation = orientation.isValidInterfaceOrientation

端末を縦方向に傾けた時にUIDevice.orientationDidChangeNotificationが呼ばれてしまっていた原因はisFlatが切り替わってしまっていることのようでした。

isFlatの値を無視して回転の判定を行いたかったので以下のように判定を入れて直接切り替わっている際は処理を行わないようにしました

  • Portraitの時はLandscapeを経由していること
  • Landscapeの時はPortraitを経由していること
// 縦向きの時に傾けた際の対応
if isFlat || isPortrait {
    if !viaLandscape {
        return
    }
}

// 横向きの時に傾けた際の対応
if isFlat || isLandscape {
    if !viaPortrait {
        return
    }
}

コード


// 回転する時に横か縦を通ったかの判定フラグ
private var viaLandscape = false
private var viaPortrait = false

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    let orientation = UIDevice.current.orientation
    let isPortrait = orientation.isPortrait
    let isLandscape = orientation.isLandscape

    viaLandscape = isLandscape
    viaPortrait = isPortrait
}

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(didChangeOrientation),
                                           name: UIDevice.orientationDidChangeNotification,
                                           object: nil)
}                                               

@objc func didChangeOrientation() {
    let orientation = UIDevice.current.orientation
    let isFlat = orientation.isFlat
    let isPortrait = orientation.isPortrait
    let isLandscape = orientation.isLandscape

    // 縦向きの時に傾けた際の対応
    if isFlat || isPortrait {
        if !viaLandscape {
            return
        }
    }

    // 横向きの時に傾けた際の対応
    if isFlat || isLandscape {
        if !viaPortrait {
            return
        }
    }
    
    // ここで処理を行う---------------
    print("hogehoge")
    // ----------------------------

    // 処理が終わった後で更新をかける
    viaLandscape = isLandscape
    viaPortrait = isPortrait
}

前の記事

IndexPathに気をつけろ!

次の記事

Slide to unlickのUIを自作する