iOS 6:一部のビューを縦向きに制限し、他のビューを回転できるようにするにはどうすればよいですか?


99

を使用しUINavigationControllerてドリルダウンインターフェイスを表示するiPhoneアプリがあります。最初のビュー、次に別のビュー、最大4レベルの深さです。最初の3つのビューを縦向きに制限し、最後のビューだけを横向きに回転できるようにする必要があります。4番目のビューから3番目のビューに戻るときに、4番目のビューが横向きだったとき、すべてを縦向きに回転させたいと思います。

iOS 5ではshouldAutorotateToInterfaceOrientation:、許容される向きに対してYESを返すように各ビューコントローラーで単純に定義しました。ビューコントローラー#4から#3に戻るときにデバイスが横向きに保持されていても、縦向きに戻ることを含め、すべてが上記のように機能しました。

iOS 6では、すべてのView Controllerが横向きに回転し、本来意図されていないものを壊します。iOS 6のリリースノートは言う

より多くの責任は、アプリとアプリのデリゲートに移動することです。現在、iOSコンテナ(などUINavigationController)は、子に問い合わせて、自動回転する必要があるかどうかを判断しません。[...]システムは、デバイスが回転するとき、またはビューコントローラーがフルスクリーンモーダルプレゼンテーションスタイルで表示されるときは常に、サポートされているインターフェイスの向きを最上部のフルスクリーンビューコントローラー(通常はルートビューコントローラー)に要求します。さらに、サポートされている方向は、このビューコントローラーがそのshouldAutorotateメソッドからYESを返した場合にのみ取得されます。[...]システムは、アプリのsupportedInterfaceOrientationsForWindow:メソッドによって返された値supportedInterfaceOrientationsと、一番上のフルスクリーンコントローラーのメソッドによって返された値を交差させることによって、向きがサポートされているかどうかを判断します。

そこで、私はをサブクラス化しUINavigationControllerMainNavigationControllerブール値のプロパティlandscapeOKを与え、これを使用しての許容可能な向きを返しましたsupportedInterfaceOrientations。次に、各View ControllerのviewWillAppear:メソッドに次のような行があります

    [(MainNavigationController*)[self navigationController] setLandscapeOK:YES];

MainNavigationControllerに望ましい行動を伝えるために。

ここで質問があります。ポートレートモードで4番目のビューに移動し、電話を裏返しにすると、横向きに回転します。次に、[戻る]ボタンを押して、ポートレートのみで機能する3番目のビューに戻ります。しかし、元に戻りません。それをどうやって行うのですか?

私は試した

    [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait]

viewWillAppear、私の3番目のビューコントローラのメソッドが、それは何もしません。これは呼び出すのに間違ったメソッドですか、それを呼び出すのに間違った場所ですか、それとも全体をまったく異なる方法で実装する必要がありますか?

回答:


95

私は同じ問題を抱えており、私のために働く解決策を見つけました。それを動作させるために、ない実装するのに十分な- (NSUInteger)supportedInterfaceOrientationsあなたUINavigationControllerに。また、コントローラー#3にこのメソッドを実装する必要があります。コントローラー#3は、コントローラー#4をポップした後、最初にポートレートのみになるメソッドです。だから、私は私のUINavigationControllerに次のコードを持っています:

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    if (self.isLandscapeOK) {
        // for iPhone, you could also return UIInterfaceOrientationMaskAllButUpsideDown
        return UIInterfaceOrientationMaskAll;
    }
    return UIInterfaceOrientationMaskPortrait;
}

ビューコントローラー#3で、以下を追加します。

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

ビューコントローラー#1、#2、および#4に何も追加する必要はありません。これは私にとってはうまくいきます。


8
あなたは人々が何ヶ月もの間解決しようとしていた何かを解決しました!あなたはこれのために多くの信用に値する!UIViewControllerのカテゴリにsupportedInterfaceOrientationsを追加しました。ポートレートのみのコントローラーのデフォルトとして完全に機能します。
12

3
SOに関する100の異なる回答の中で、これは実際に機能するものです。ありがとう:-)
Christer Nordvik

16
私はこれを設定しましたが、ポートレートからビューコントローラーに戻るときに、向きがランドスケープのみに制限されていますが、間違った方向になっています。それは風景に自動回転しません-他にまだそれを見ている人はいますか?
Oded Ben Dov

1
必要な風景ビューコントローラがセグエから提示されていて、ナビゲーションコントローラがない場合はどうなりますか?
jesses.co.tt 2013

1
戻り値の型として、最後のものを編集してNSUIntegerにします。
2014年

68

CustomNavigationControllerを追加する

その中でこれらのメソッドをオーバーライドします。

-(BOOL)shouldAutorotate
{
    return [[self.viewControllers lastObject] shouldAutorotate];
}

-(NSUInteger)supportedInterfaceOrientations
{
    return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}

次に、plistにすべての向きを追加します

ここに画像の説明を入力してください

ビューコントローラーで、必要なものだけを追加します。

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

これらのメソッドは、ナビゲーションコントローラメソッドをオーバーライドします


5
あなたは本当にこの答えでそれを釘付けにした!どうもありがとうございます。あなたは本当にたくさん助けてくれました。これはしばらくの間私にとって大きな問題でした。よくやった。
K2Digital

ありがとうございました!これは私が理解するのにとても多くの時間とエネルギーを要しました!
bkbeachlabs 2013年

あなたは命の恩人です...これは私のアプリでも動作します。実際、私がアプリで必要なのは、すべてViewController縦向きモードで、ViewControllerLandscap&Portraitモードで操作することです。また、iOS 5.0とiOS 6.0の両方でサポートを提供しています。そのため、私は回避策を講じることができませんが、これは素晴らしいソリューションです。ルートビューコントローラーにCustomNavigationControllerを追加し、必要に応じてサポートを提供します。もう一度ありがとう。
Bhavin_m 2013年

素晴らしい。まさに私が必要としたもの。ありがとうございました。
mszaro 2013

必要な風景ビューコントローラがセグエから提示されていて、ナビゲーションコントローラがない場合はどうなりますか?
jesses.co.tt 2013

9

SOに関する無数の同様の質問のすべての回答を調べた後、どの回答も私にとってうまくいきませんでしたが、それらは私にいくつかのアイデアを与えました。これが私が問題を解決した方法です:

まず、プロジェクトのターゲットでサポートされているインターフェイスの向きに、回転ビューに必要なすべての向きが含まれていることを確認します。

ここに画像の説明を入力してください

次に、次のカテゴリを作成しますUINavigationController(Appleはサブクラス化しないと言っているため):

@implementation UINavigationController (iOS6AutorotationFix)

-(BOOL)shouldAutorotate {
    return [self.topViewController shouldAutorotate];
}

@end

そのカテゴリと、回転できるようにしたいビューコントローラー(これをと呼びますRotatingViewController)を、ナビゲーションコントローラーを含む最上位のビューコントローラーにインポートします。そのビューコントローラーで、shouldAutorotate次のように実装します。これは、回転するビューコントローラーと同じであってはならないことに注意してください。

-(BOOL)shouldAutorotate {

    BOOL shouldRotate = NO;

    if ([navigationController.topViewController isMemberOfClass:[RotatingViewController class]] ) {
        shouldRotate = [navigationController.topViewController shouldAutorotate];
    }

    return shouldRotate;
}

最後に、あなたの中でRotatingViewController、実装shouldAutorotatesupportedInterfaceOrientations次のように:

-(BOOL)shouldAutorotate {
    // Preparations to rotate view go here
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAllButUpsideDown; // or however you want to rotate
}

これを行う必要がある理由は、iOS 6がトップビューコントローラではなくルートビューコントローラに回転の制御を提供するためです。個々のビューの回転をスタック内の他のビューとは異なる動作にしたい場合は、ルートビューコントローラーで特定のケースを書き込む必要があります。


2
これはより多くの愛に値する素晴らしい答えです。これは、他のどこにも見つからなかった重要な情報 です。個々のビューの回転をスタック内の他のビューとは異なる動作にしたい場合は、ルートに特定のケースを記述する必要がありますビューコントローラ
shmim 2014年

これは、アプリのストーリーボードの最初のメンバーとしてnavigationControllerを初期コントローラーとして使用している場合に機能すると思いますが、初期コントローラーが単にViewControllerである場合はどうでしょうか。
Duck

4

@Brianの回答にコメントする評判が足りないので、ここにメモを追加します。

ブライアンは、iOS6が回転制御をrootViewControllerに与えると述べました-これは、言及されたUINavigationControllerだけでなく、私にとってはUITabBarControllerでもあり得ます。私の構造は次のようになります:

  • UITabBarController
    • UINavigationController
      • UIViewControllers ...
    • UINavigationController
      • UIViewControllers ...

したがって、メソッドを最初にカスタムUITabBarControllerに追加し、次にカスタムUINavigationControllerに追加し、最後に特定のUIViewControllerに追加しました。

UITabBarControllerおよびUINavigationControllerの例:

- (BOOL)shouldAutorotate {
    return [self.viewControllers.lastObject shouldAutorotate];
}

- (NSUInteger)supportedInterfaceOrientations {
    return [self.viewControllers.lastObject supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return [self.viewControllers.lastObject shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.viewControllers.lastObject preferredInterfaceOrientationForPresentation];
}

実際には、ルートビューコントローラーのインスタンス変数としてUITabBarControllerを使用しました。これは、6.0より前のiOSバージョンではUITabBarControllerをサブクラス化する必要がなく、iOS 5.0に戻す必要があるためです。
ブライアン

@micmdk同じ問題があります。解決方法を教えてください。コードをコピーして、カスタムナビゲーションコントローラーとカスタムUabbarControllerに入れ、他のビューコントローラーをブライアンのガイド付きとして設定しました。しかし、縦長の固定ビューコントローラーをまだ取得していません。
Ram S

@ブライアン私は同じ問題を抱えています。解決方法を教えてください、あなたのコードをコピーしてカスタムナビゲーションコントローラーとカスタムUabbarControllerに入れ、他のビューコントローラーをMicmdkのガイド付きに設定しました。しかし、ios8では縦長の固定ビューコントローラーを取得できません。
Ram S

3

自分の質問に部分的に答えたいと思います。viewWillAppear3番目ののメソッドで使用されている次のコード行が機能することがわかりましたUIViewController

[[UIDevice currentDevice] 
      performSelector:NSSelectorFromString(@"setOrientation:") 
           withObject:(id)UIInterfaceOrientationPortrait];

しかし、私はこのソリューションがあまり好きではありません。Appleのドキュメントによると、デバイスの物理的な向きを表す読み取り専用プロパティに割り当てるためにトリックを使用しています。これは、ユーザーの手で正しい向きにジャンプするようにiPhoneに指示するようなものです。

単純に機能するので、これをアプリに残したくなります。しかし、それは正しくないと思うので、私は問題を未解決の解決策のために開いたままにしたいと思います。


4
アプリをそのままにしておくと、アプリが拒否される可能性が高くなります。私もこれに対する解決策を探していますが、それは不可能のようです。stackoverflow.com/questions/7196300/…この質問への答えを見つけることができます。それは一種の動作ですが、何らかの理由で私のビューコントローラーの1つでボタンタッチ処理が壊れています
Lope

私はチャンスを利用してAppleに提出しました。まだレビューを待っています...投稿し続けます。
HaraldBögeholz2012年

Appleは、このハックを配置して私のアプリを承認しました。私はそれが将来のiOSアップデートで私に逆火をしないことを望みます。それまでの間、私はまだクリーンソリューションの提案を受け付けています!
HaraldBögeholz12年

1
@HaraldBögeholzGrtの提案。しかし、ARCを使用して新しいxCodeで作業しているときは、ansを使用できません。「NSInteger(別名int)へのキャストはARCで許可されていません。PerformSelectorはセレクタが不明なためリークを引き起こす可能性があります」これを解決するにはどうすればよいですか?私たちが解決策を得た場合、私はあなたに本当に慣れています。ARCを特定のビューでオフにすると、リークデータが原因でアプリがクラッシュする。私に提案してください
Hitarth

4
プライベートAPIの使用は決して解決策ではありません。
Javier Soto

2

これは私がiOS 6.0の向きのサポートに使用しています

-(BOOL)shouldAutorotate{
    return YES;
}  

 - (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskAll;
}


- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{  
  return UIInterfaceOrientationPortrait;
}

1

これが非常によく見られるスレッドであること。一番簡単な答えだと思います。これはios8以降で機能します

-(BOOL)shouldAutorotate
{
    return YES;
}

そして

-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}

それでおしまい。楽しい!

ああ、ViewControllerはナビゲーションコントローラに埋め込まれています。ナビゲーションコントローラは、サブクラス化したり、構成したりする必要がありません。


0

これは誰にとってもうまくいくとは限りませんが、私にとってはうまくいきます。実装する代わりに...

[(MainNavigationController*)[self navigationController] setLandscapeOK:YES];

私のすべてのコントローラでviewWillAppearで、私はオーバーライドすることにより、私UINavigationControllerサブクラスのこのプロセスの内部を一元化することを決めたUINavigationControllerDelegate方法をnavigationController:willShowViewController:animated:

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {

    self.previousVCLandscapeOK = self.isLandscapeOK; // Store the current VC's orientation preference before pushing on the new VC so we can set this again from within the custom "back" method
    self.isLandscapeOK = NO; // Set NO as default for all VC's
    if ([viewController isKindOfClass:[YourViewController class]]) {
        self.isLandscapeOK = YES;
    }
}

ナビスタックからVCをポップするときに、このデリゲートメソッドが呼び出されないことを発見しました。UINavigationControllerサブクラス内から戻る機能を処理しているため、これは問題ではありませんでした。これにより、特定のVCに適切なナビゲーションバーボタンとアクションを設定できます...

if ([viewController isKindOfClass:[ShareViewController class]]) {

    UIButton* backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 57, 30)];
    [backButton setImage:[UIImage imageNamed:@"back-arrow"] forState:UIControlStateNormal];
    [backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem* backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    viewController.navigationItem.leftBarButtonItem = backButtonItem;

    UIImageView* shareTitle = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"share-title"]];
    [shareTitle setContentMode:UIViewContentModeScaleAspectFit];
    [shareTitle setFrame:CGRectMake(0, 0, shareTitle.frame.size.width - 10, shareTitle.frame.size.height - 10)];
    viewController.navigationItem.titleView = shareTitle;

} else if(...) {
    ...
}

backスタックからのVCのポップを処理し、適切なローテーション設定を設定するための私のメソッドは次のようになります...

- (void)back {
    self.isLandscapeOK = self.previousVCLandscapeOK;
    self.previousVCLandscapeOK = NO;
    [self popViewControllerAnimated:YES];
}

ご覧のとおり、基本的には最初に2つのプロパティを設定するだけです...

@property (nonatomic) BOOL isLandscapeOK;
@property (nonatomic) BOOL previousVCLandscapeOK;

navigationController:willShowViewController:animated:そのサポートの方向が提示されようとしていることVC内にあるかを決定します。VCをポップすると、カスタムの「戻る」メソッドが呼び出され、isLandscapeOKを、previousVCLandscapeOKの値を介して保存されたものに設定します。

言ったように、これはすべての人にとってうまくいくわけではないかもしれませんが、私にとってはうまく機能し、コードを各ビューコントローラーに追加することを心配する必要はありません。

これが私と同じように誰かを助けることを願っています。ありがとう、ジェレミー。



0

iOS 6アプリの縦向きのみを強制したい場合は、メソッドの下のUIViewControllerサブクラスに追加できます

- (BOOL)shouldAutorotate {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return YES;
    } else {
        return NO;
    }
}


- (NSUInteger)supportedInterfaceOrientations {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return UIInterfaceOrientationMaskAll;
    } else {
        return UIInterfaceOrientationMaskPortrait;
    }
}

2
問題は、そのビューがルートビュー(この質問にはありません)でない限り、shouldAutorotateが呼び出されないことです。
ブライアン

なんらかの理由で、戻り値の型がNSUInteger正しいvar型であっても警告がスローされます。UIInterfaceOrientationMask代わりにOCDを使用している場合。
クリスJ

また、このソリューションは、UINavigationControllerをサブクラス化する必要がなく、モーダルビューコントローラー(カメラビュー)に対して完全に機能しました。モーダルは独立して扱われると思います。元の質問には関係ありませんが、デザインを賢くするのに役立つ情報です。
クリスJ

0

1つを除いてすべてのVCを縦向きにロックしたかったのです。これは私のために働いたものです。

  1. すべての方向のサポートをplistファイルに追加します。
  2. ルートビューコントローラーで、ウィンドウの上にあるビューコントローラーの種類を検出し、それに応じてsupportedInterfaceOrientationsメソッドでアプリの向きを設定します。たとえば、ウェブビューがスタックの一番上にあるときのみ、アプリを回転させる必要がありました。これが私のrootVCに追加したものです:

    -(NSUInteger)supportedInterfaceOrientations
    {
        UIViewController *topMostViewController = [[Utils getAppDelegate] appNavigationController].topViewController;
        if ([topMostViewController isKindOfClass:[SVWebViewController class]]) {
            return UIInterfaceOrientationMaskAllButUpsideDown;
        }
        return UIInterfaceOrientationMaskPortrait;
    }

Trunal、これは数か月前のものですが、上部にあるビューコントローラーの種類を検出する方法についてもう少し詳しく教えてください。具体的には、ここで何をしているのかわかりません[[Utils getAppDelegate] appNavigationController]。私が推測しているUtilsはあなたが持っているいくつかのクラスですが、私はあなたがその中で何をしているのか途方に暮れています。どんな助けでも素晴らしいでしょう!
ベン・

0

@micmdkの返信で@Ram Sの質問に答える評判が足りないので、ここにメモを追加します。

UITabbarControllerを使用する場合は、次のように、@ micmdkのコードのself.viewControllers.lastObjectをself.selectedViewControllerに変更してみてください。

- (BOOL)shouldAutorotate {
    return [self.selectedViewController shouldAutorotate];
}

- (NSUInteger)supportedInterfaceOrientations {
    return [self.selectedViewController supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return [self.selectedViewController shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}

0

アプリのPLISTで定義されている方向とは異なる方向で表示する必要があるすべてのView Controllerクラスで、shouldAutorotate()およびsupportedInterfaceOrientations varを実装してオーバーライドできます。

ただし、自明ではないユーザーインターフェイスでは、数十のクラスにそれを追加すると問題が発生する可能性があり、それらすべてをサポートするいくつかの共通のサブクラス(MyBaseTableViewController、MyBaseNavigationController、MyBaseTabBarController)にしたくない場合があります。

UIViewControllerでこれらのメソッド/変数を直接オーバーライドすることはできないため、UITableViewController、UINavigationController、UITabBarControllerのような通常は基本クラスであるそのサブクラスでそれを行うことができます。

したがって、いくつかの拡張機能を実装し、MyPreciousViewControllerを設定して、このSwift 4コードスニペットのように他のすべての方向とは異なる方向で表示することができます。

extension UITableViewController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

    if let last = self.navigationController?.childViewControllers.last,
        last != self {
            return last.supportedInterfaceOrientations
    } else {
        return [.portrait]
    }
    }
}

extension MyPreciousViewController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

    return [.portrait,.landscape]
    }
}


extension UINavigationController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        return [.portrait]
    }
}


extension UITabBarController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        return [.portrait]
    }
}

0

同じ種類の問題を解決しました。

を使用しUINavigationControllerてビューコントローラをプッシュする場合は、以下のメソッドを設定する必要があります。

extension UINavigationController{

    override open var shouldAutorotate: Bool {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return true
        }
        return false
    }

    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return .portrait
        }
        return .landscapeRight

    }
    override open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return .portrait
        }
        return .landscapeRight
    }
}

見せたいLoginViewController使用場所にUIViewController。私の場合、表示したいLoginViewControllerPortrait、他のモードViewControllerslandscapeのモード。


-1

この問題を解決するには、次の方法を使用してください

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

あなたが望む向きだけを返す!!!


3
私が書いたように、私はそれをしました。これにより、ビューコントローラーが回転しないようにしますが、ビューコントローラーに戻ったときに、サポートされている唯一の向きに強制的に戻すことはありませ
HaraldBögeholz12年

これは私にはうまくいきません。これをビューコントローラに含めても、ビューは回転します。何ができますか?
シム

なぜmaxfiresolutionsに反対票を投じるのですか?それは私のために働き、@ Floによって与えられた同じ方法は多くの賛成票を持っています。
ViruMax 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.