Developer's Blog

iOS の自動回転を使わずに4方向対応アプリを作成する方法

以前、「より”紙”ライクな Inkiness for iPad の回転」で、Inkiness for iPad の回転についての話をしました。Inkiness for iPad では、iOS の自動回転機能を使わずに4方向に対応させています。今回は、その手法をご紹介しようと思います。

なぜ自動回転を使わないのか

Inkiness for iPad では、描いている絵や背景はそのままで、コントロール類のみを回転させます。自動回転を使っても、回転させたく無い要素を逆に回転させれば、コントロール類のみの回転を実現できます。しかしただ一つだけ問題がありました。それが回転時に出現する黒い枠です。たとえ背景が透明だろうとこれは出ます。なのでしかたなく自動回転を使わないことにしました(実際は少し利用します)。

自動回転を使わないことによる問題

自動回転を使わないとどうしようもない問題があります。それはキーボードとモーダルとポップオーバーの向きです。これらは現在の View Controller の interfaceOrientation に従って向きが決まるようです。ポップオーバーは似たようなものを自分で作ってしまえばいいのですが(Inkiness はそうしてます)、メール機能はモーダルを使わない限り実現できません。手書きメモアプリのためにキーボードを自前で作るのはさすがにないですよね。なので、実は少しだけ自動回転を使います。

自動回転しつつ黒い枠を出さない方法

  1. Window にダミーの View Controller の view を addSubview する
  2. 同じ Window に本物の View Controller の view を addSubview する
  3. ダミーの View Controller は shouldAutoRotateToInterfaceOrientation で YES を返す
  4. 本物の View Controller は shouldAutoRotateToInterfaceOrientation で NO を返す

この4ステップをすれば、本物の view は回転せずに、裏でダミーだけが回転する状況になります。しかもダミーの上に本物が完全にのっかているので黒い枠がでません。アプリ自体はダミーをメインの View Controller だと思っているので、本物側で出したキーボードもダミーの回転に合わせて回転します。また ダミーの View Controller に対してモーダルをだせば、そのモーダルは回転してくれます。

回転の Notification を投げて自前で回転させる

本物の View Controller は自動回転しないので、各ビューを自前で回転させる必要があります。そこで、せっかくいるダミーの View Controller に willRotateToInterfaceOrientation のタイミングで Notification を post してもらうようにします。それをメインのビューコントローラが受け取ることで、同じように回転のイベントを受けられます。しかも、willRotateToInterfaceOrientation の時の duration に回転アニメーションにかかる時間が入っているので、受け取った側で同じ秒数のアニメーションをすれば、キーボードなどの回転に合わせてきれいに動いてくれます。

ダミーの View Controller

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return YES;
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 
                                duration:(NSTimeInterval)duration {
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                              [NSNumber numberWithInteger:toInterfaceOrientation],
                              @"interfaceOrientation",
                              [NSNumber numberWithFloat:duration],
                              @"duration", nil];
    [[NSNotificationCenter defaultCenter] 
        postNotificationName:@"InterfaceOrientationWillChange" 
                      object:nil 
                    userInfo:userInfo];
}

本物の View Controller

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return NO;
}

- (void)interfaceOrientationWillChange:(NSNotification *)notification {
    UIInterfaceOrientation interfaceOrientation = 
        [[[notification userInfo] 
             objectForKey:@"interfaceOrientation"] integerValue];
    CGFloat duration = 
        [[[notification userInfo] objectForKey:@"duration"] floatValue];
    
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:duration];
    switch (interfaceOrientation) {
        case UIInterfaceOrientationPortrait:
            rotateView.transform = CGAffineTransformMakeRotation(0);
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            rotateView.transform = CGAffineTransformMakeRotation(M_PI);
            break;
        case UIInterfaceOrientationLandscapeLeft:
            rotateView.transform = CGAffineTransformMakeRotation(- M_PI / 2);
            break;
        case UIInterfaceOrientationLandscapeRight:
            rotateView..transform = CGAffineTransformMakeRotation(M_PI / 2);
            break;
        default:
            break;
    }
    [UIView commitAnimations];
}

OS の自動回転サポートは素晴らしいです。色々なことをよしなにやってくれます。この方法を使うと、要素それぞれの回転の面倒を見る必要があるので大変ですが、見た目を良くするために黒い枠を消したいこともあるかと思います。その時は、この方法を検討してみてください。

Copyright © 2019 Fenrir Inc. All rights reserved.