NSNotificationCenterでデバイスローテーションを通知してもらう際の注意点

ViewController以外でデバイスのローテートを検出する場合、NSNotificationCenterにお願いするのが手っ取り早い方法だ。

コード自体は非常に簡単。

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];

- (void)didRotate:(NSNotification*)notification
{
    UIDeviceOrientation orientation = [[notification object] orientation];
   //ローテート時の処理
    〜
}

このようにNSNotificationクラスのパラメタ只一つをシグネチャに持つセレクタを指定してやれば、デバイス回転時にdidRotateが呼ばれて万々歳のはず...なのだが

UIDeviceOrientationPortrait
UIDeviceOrientationPortraitUpsideDown
UIDeviceOrientationLandscapeLeft
UIDeviceOrientationLandscapeRight
UIDeviceOrientationFaceUp
UIDeviceOrientationFaceDown

これらの定義された状態が不定期に(それも結構頻繁に)飛んでくる。更には無効であるはずのUIDeviceOrientationUnknownもばんばん通知される。フィルタされていて閾値を超えた所でどれか一つが飛んでくる、という訳では無いようで、例えばポートレイトとランドスケイプだけを判定するためには不要な値をパスする必要がある。

ポートレイトとランドスケイプだけを処理するdidRotate

UIDeviceOrientation lastOrientation;

- (void)didRotate:(NSNotification*)notification
{
    UIDeviceOrientation orientation = [[notification object] orientation];
        
    if ( [self isDevicePortrait:orientation] || [self isDeviceLandscape:orientation] )
    {
        if ( lastOrientation != orientation )
        {
            //ローテート時の処理
            lastOrientation = orientation;
        }
    }
}
- (BOOL)isDevicePortrait:(UIDeviceOrientation)orientation 
{
    return (orientation == UIDeviceOrientationPortrait ||
            orientation == UIDeviceOrientationPortraitUpsideDown);
}
- (BOOL)isDeviceLandscape:(UIDeviceOrientation)orientation 
{
    return (orientation == UIDeviceOrientationLandscapeLeft ||
            orientation == UIDeviceOrientationLandscapeRight);
}

まさか回転の検出でフィルタがいるとは思わなかった。