web-dev-qa-db-ja.com

iOS:サブビューの自動回転を無効にする

向きの変更をサポートするiPadアプリケーションのネストされたビュー階層があります。次のように見えます。

UIViewController
    UIView
        - UIView
            - UIImageView (disable rotation)
            - UIImageView
            - UIView (disable rotation)
        - UIView
        - UIView
        ...

一部のサブビューの向きをロックし、他のサブビューは自動回転とサイズ変更を許可したいと思います。私はこれを達成する方法を完全に理解することができないようです。

1つのアプローチは、willAnimateRotationToInterfaceOrientation:内でサブビューを手動で回転させることのようです。 SDKが、元に戻すだけのローテーションを実行していることを考えると、これは特に魅力的ではありません。

サブビューの向きの変更を無効にする方法や、階層を再構築するその他の方法はありますか?

18
Jason George

自動回転はビューのUIViewController(shouldAutorotateToInterfaceOrientation:)によって処理されるため、1つのアプローチは、回転可能なビューが1つのビューコントローラーによって管理され、回転不可能なビューが別のビューコントローラーによって管理されるように階層を配置することです。次に、これらのUIViewControllerのルートビューの両方をウィンドウ/スーパービューに追加する必要があります。

ここでの微妙な点は、同じレベルに2つのビューコントローラーのビューがある場合(つまり、addSubview:を介して追加された場合)、最初のビューコントローラー(通常はウィンドウのrootViewController)のみがshouldAutorotateToInterfaceOrientation:メッセージを受信することです。

私はこのアプローチを自分で使用して、メインビューではなく回転するツールバーを実現しました。

AppleのテクニカルQ&A QA1688(「UIViewControllerがデバイスと一緒に回転しないのはなぜですか?」) この問題について少し説明します。


iOS 6のアップデート:

自動回転は、UIViewControllershouldAutorotateおよびsupportedInterfaceOrientationsメソッドを使用するようになりました。 shouldAutorotateはデフォルトでYESを返しますが、ビューがウィンドウの直接サブビューであるrootViewController以外のビューコントローラは、とにかく回転コールバックを受信しないことに注意してください。


iOS 6のサンプルコード:

「シングルビューアプリケーション」テンプレートを使用して新しいプロジェクトを作成し、「ストーリーボードを使用する」がチェックされていることを確認します。提供されているViewControllerクラスを回転ビューコントローラーとして使用し(必要に応じて名前を変更します)、UIViewControllerという2番目のNonRotatingViewControllerサブクラスを作成します。このViewControllerはローテーションコールバックを受信することもありませんが、完全性と明確さのために、NonRotatingViewController.mに次のコードを追加します。

- (BOOL)shouldAutorotate
{
    return NO;
}

MainStoryboardファイルで、新しいView Controllerオブジェクトをドラッグして、そのクラスをNonRotatingViewControllerに設定し、ストーリーボードIDを「NonRotatingVC」に設定します。そこにいる間に、回転ビューコントローラビューの背景色をクリアに変更し(非回転ビューがこのビューの下に追加されます)、各ビューにラベルを追加します。 AppDelegate.mに、次のコードを追加します。

#import "NonRotatingViewController.h"

// ...
// ...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    NonRotatingViewController *nonRotatingVC = [mainStoryboard instantiateViewControllerWithIdentifier:@"NonRotatingVC"];
    [self.window addSubview:nonRotatingVC.view];
    return YES;
}

これは、回転しないビューコントローラをインスタンス化し、そのビューをウィンドウに直接追加するだけです(N.B.この時点で、ウィンドウのrootViewControllerはストーリーボードによってすでに設定されています)。

プロジェクトを実行します。デバイスを回転させ、一方のラベルが回転し、もう一方のラベルが静止しているのを見て驚嘆してください。


iOS 6より前のサンプルコード:

私はこれを新しいプロジェクトで行いました-新しいビューベースのアプリケーションは問題なく動作します。 RotatingViewControllerNonRotatingViewControllerの2つの新しいViewControllerを追加します。それぞれのペン先の内側に、ビューを回転させるかどうかを説明するラベルを追加しました。次のコードを追加します。

'RotatingViewController.m'

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


'NonRotatingViewController.m'

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if (interfaceOrientation == UIInterfaceOrientationPortrait) {    // Or whatever orientation it will be presented in.
        return YES;
    }
    return NO;
}


'AppDelegate.m'

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    RotatingViewController *rotating = [[RotatingViewController alloc] initWithNibName:@"RotatingViewController" bundle:nil];
    self.rotatingViewController = rotating;
    [rotating release];

    NonRotatingViewController *nonRotating = [[NonRotatingViewController alloc] initWithNibName:@"NonRotatingViewController" bundle:nil];
    self.nonRotatingViewController = nonRotating;
    [nonRotating release];

    [self.window addSubview:self.rotatingViewController.view];
    [self.window insertSubview:self.nonRotatingViewController.view belowSubview:self.rotatingViewController.view];

    [self.window makeKeyAndVisible];

    return YES;
}

これがお役に立てば幸いです。

22
Stuart

これが別の方法です。このコードをviewControllerviewDidLoadに配置するだけです。

    YourAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
        // the view you don't want rotated (there could be a heierarchy of views here):
    UIView *nonRotatingView = [[UIView alloc] initWithFrame:CGRectMake(100,0,30,500)];
    nonRotatingView.backgroundColor = [UIColor purpleColor];

        // make sure self.view and its children are transparent so you can see through to this view that will not be rotated behind self.view.

    [delegate.window insertSubview:nonRotatingView  belowSubview:self.view];

        // you can't put it in the front using:
        //    [delegate.window insertSubview:nonRotatingView aboveSubview:self.view];
        // It will show up the same as before

    // you should declare nonRotatingView as a property so you can easily access it for removal, etc.
10
mahboudz

この問題を解決する私のアプローチは、UINotificationを使用して自動回転を検出し、ビューを反対方向に回転させる対策を行うことです。

ここで完全なコードを見つけてください:
https://Gist.github.com/ffraenz/5945301

- (void)orientationDidChangeNotificationReceived:(NSNotification *)notification
{
    // find out needed compass rotation
    //  (as a countermeasure to auto rotation)
    float rotation = 0.0;

    if (self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
        rotation = M_PI;
    else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
        rotation = M_PI / 2.0;
    else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight)
        rotation = M_PI / (- 2.0);

    // rotate compass without auto rotation animation
    //  iOS rotation animation duration is 0.3
    //  this excludes the compassView from the auto rotation
    //  (same effect as in the camera app where controls do rotate and camera viewport don't)
    [UIView animateWithDuration:0.3 animations:^(void) {
        self.compassView.transform = CGAffineTransformMakeRotation(rotation);
    }];
}
4
ffraenz