1

これは私のコードです。

 SomeController *analyticsViewController = [[SomeController alloc] init];

 MTNavigaionLandscapeViewController *analyticsNavigaionObject =  [[MTNavigaionLandscapeViewController alloc] initWithRootViewController:analyticsViewController];

 [analyticsNavigaionObject setNavigationBarHidden:YES];

 if ([self respondsToSelector:@selector(presentModalViewController:animated:completion:)]) {
      [self presentViewController:analyticsNavigaionObject animated:YES completion:nil];
  } else {
          [self presentModalViewController:analyticsNavigaionObject animated:YES];
  }
  [analyticsViewController release];
  [analyticsNavigaionObject release];

SomeController.m でサポートされている向きは次のとおりです。

- (NSUInteger)supportedInterfaceOrientations
{
 return UIInterfaceOrientationMaskLandscape;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
  return UIInterfaceOrientationLandscapeRight;
}

MTNavigaionLandscapeViewController (UINavigationController のサブクラス) でサポートされている向き

- (NSUInteger)supportedInterfaceOrientations
{
 return UIInterfaceOrientationMaskLandscape;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
 return UIInterfaceOrientationLandscapeRight;
}

最後に、SomeController のみを提示すると、正常に動作します。私の問題は、Navigationcontroller を提示すると問題が発生することです。私を助けてください

4

2 に答える 2

2

iPhone を強制的に特定の向きに回転させる方法。

問題

iPhone および iPad アプリは、縦向き、横向き、またはその両方をサポートできます。場合によっては、1 つまたはごく少数のビューを縦向きまたは横向きのみで表示する必要がある場合や、残りのアプリがサポートしていない向きで表示する必要がある場合があります。iOS または cocoa フレームワークはそれぞれ、サポートされている方向をビューごとに個別に定義するメソッドを提供します。ただし、すべてのアプリに制限が適用され、プロジェクト レベルまたはターゲット レベルでそれぞれ定義されている場合を除き、ローテーションを実行する必要があるのは常にユーザーです。アプリは、特定の向きをサポートするかどうかを判断できますが、システムを実際に回転させることはできません。

このチュートリアルでは、この不十分さを克服する方法を示します。

与えられた例は、1 つを除くすべてのビュー コントローラーを縦向きモードで表示する iPhone アプリ用です。優れた設計と使いやすさの理由から、View Controller の 1 つはランドスケープでのみ機能します。

これは今のところ iPhone iOS 6 で証明されています。

モーダルに表示されたビュー コントローラーの回転

ビューがモーダルに表示されている場合、このタスクは簡単です。モーダルに表示されるビュー コントローラーの場合、表示されるビュー コントローラーは横向きのみをサポートする必要があり、ビューをモーダルに表示する前に、ビュー コントローラーの向きを横向きに設定する必要があります。

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft animated:YES];
[self presentModalViewController:landscapeVC animated:YES];

そうすることで、View ControllerlandscapeVCは横向きで表示されます。

しかし、ナビゲーションバーなどを保持するには、ナビゲーションコントローラーのスタックにプッシュしたい。残念ながら、ユーザーが実際に回転させない限り、デバイスの向きは回転しません。

さらに、私のアプリには 2 つのターゲットがあります。そのうちの 1 つのナビゲーションはタブ バーに基づいており、もう 1 つのターゲットのナビゲーション (機能により無料版が縮小されます) はプレーンです。したがって、私のソリューションは、タブバーのあるアプリとないアプリの両方に適合する必要があります。

したがって、このソリューションは、タブ バーしかない、またはまったくないアプリでは大きすぎる可能性があります。しかし、それはどんな場合でも機能します。

タブバーコントローラーの向きの問題

タブ バーにビュー コントローラーが表示されている場合、向きを制御するのはタブ バー コントローラーです。実際には、内部で提示されるすべてのビューがすべて同じ方向をサポートすることになっていることを意味します。これを克服するにUITabBarは、タブ バーが横向きをサポートするかどうかを制御する機能をサブクラス化して導入する必要があります。

アプリケーション デリゲートの変更

私の場合、ブール変数をアプリケーション デリゲートに追加しました。これは、タブ バーを使用する場合と使用しない場合の二重性によるものです。タブ バーしかない場合は、その変数をプロパティとしてタブ バー サブクラスに追加することができます。本から外れたOOPの場合、シングルトンがいいでしょうが、単一のブールスイッチには少し大きすぎると思いました。

AppDelegate.h:

@property BOOL  isLandscapePreferred;

自動合成です。デフォルトは ですNO。ただし、アプリのデリゲートでNO明示的に設定したい場合があります。application:didFinishLoadingWithOptions:

タブバーなしで動作するターゲットの場合、すべてのビュー コントローラーがその設定に応答する必要があります。基本的に、横向きのビューを表示しているものは、次を実装する必要があります。ただし、アプリ内に複数のビューがあり、横向きで表示する必要がある場合は特に、アプリ全体に害はありません。

UIApplication の変更

Web で見つけたいくつかのアドバイスに従って、UIApplication をサブクラス化し、supportedInterfaceOrientationsそこに上書きすることにしました。ただし、xcode のターゲットの [概要] ペインでサポートされているすべての方向をマークしましたが、その設定は UIApplication によって上書きされます。

MyApp.h と MyApp.m:

#import <UIKit/UIKit.h>

@interface MyApp : UIApplication

@end

#import "MyApp.h"
#import "AppDelegate.h"

@implementation MyApp

- (NSUInteger)supportedInterfaceOrientationsForWindow:(UIWindow *)window {

    if ([(AppDelegate*) [[UIApplication sharedApplication] delegate] isLandscapePreferred]) {
        return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeLeft);
        NSLog(@"PhotocollApp: Landscape");
    } else {
        return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
        NSLog(@"PhotocollApp: Portrait");
   }

}

@end

メインから MyApp を呼び出す

次に、サブクラス化されたアプリケーション オブジェクトがアプリケーションの起動時にインスタンス化されるようにする必要があります。これには、main.m の変更が必要です。

#import <UIKit/UIKit.h>

#import "AppDelegate.h"
#import "PhotocollApp.h"

int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, NSStringFromClass([MyApp class]), NSStringFromClass([AppDelegate class]));
    }
}

UIApplicationMainには、受け取った 2 つの引数が渡さargcれ、最初の 2 つの引数に、アプリケーションのクラスの名前を表すargv2 つのオブジェクトとアプリ デリゲート クラスが続きます。NSStringアプリケーション デリゲートの標準名は変更しませんでしたが、ここで独自のアプリケーション クラスを識別する必要があります。

すべてのビュー コントローラで supportedInterfaceOrientations を上書きする

と の「抽象」サブクラスを導入UIViewControllerUITableViewControllerました。私のView Controllerはすべてそれらを継承しています:

MyRotatingViewController.h および .m:

#import <UIKit/UIKit.h>

@interface MyRotatingViewController : UIViewController

@end

#import "MyRotatingViewController.h"
#import "AppDelegate.h"

@implementation MyRotatingViewController

// … 

#pragma mark - Rotation Management

- (BOOL) shouldAutorotate {

    return NO;
}

- (NSUInteger)supportedInterfaceOrientations {

    if ([(AppDelegate*) [[UIApplication sharedApplication] delegate] isLandscapePreferred]) {
        return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeLeft);
    } else {
        return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
    }

}

@end

についてもまったく同じですMyRotatingTableViewController。唯一の違いは .h ファイルにあります。から当然継承していUITableViewControllerます。

#import <UIKit/UIKit.h>

@interface MyRotatingTableViewController : UIViewTableController

@end

MyRotatingViewControllerすべての (影響を受ける) ビュー コントローラーがそれぞれまたはから継承されていることを確認してくださいMyRotatingTableViewController。この「抽象」クラスを実装する代わりに、もちろんshouldAutorotate関連supportedInterfaceOrientationsするすべてのビュー コントローラで実装することもできます。

カスタム タブ バー クラス

前述のように、タブ バー駆動型アプリでは、方向設定を制御するメソッドを持つのはタブ バーであり、タブ バーによって提示されるビュー コントローラーのメソッドではありません。

//  MyTabBarController.h
#import <UIKit/UIKit.h>

@interface MyTabBarController : UITabBarController

@end

//  MyTabBarController.m

#import "MyTabBarController.h"

#import "AppDelegate.h"

@interface MyTabBarController ()

@end

@implementation MyTabBarController

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

#pragma mark - Rotation Management

- (BOOL) shouldAutorotate {

    return YES;
}

- (NSUInteger)supportedInterfaceOrientations {


    if ([(AppDelegate*) [[UIApplication sharedApplication] delegate] isLandscapePreferred]) {
        return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeLeft);
    } else {
        return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
    }

}
@end

MyTabBarControllerストーリーボードを使用しているので、IB 内の唯一のタブ バー オブジェクトのプロパティ ペインで設定する必要があります。プログラムで作成する場合は、MyTabBarController代わりにインスタンス化するだけですUITabBarController

横向きに強制されたView Controllerを呼び出す

80/20 ルールによれば、20% の実際の作業に対して 80% の準備ができただけです。 いよいよ本番です…

これは、ランドスケープビューがプッシュされるビューコントローラーのまさにそのメソッドの真ん中にあります。私の場合、これはtableView:didSelectRowAtIndexPath:実装にあります。IBActionもちろん、メソッド内またはメソッド内にある可能性がありますprepareForSegue:sender:。これを行うと、prepareForSegue実行時にエラー コンソールに警告が表示される危険があります。誤動作は見たことがありませんが、アプリがストアに提出されたときにこれが合格するか拒否されるかはわかりません。

秘訣は、ステータス バーの向きを横向きに設定してから、View Controller をモーダルに表示することです。これで、デバイスは横向きモードになりました。ユーザーはこれまで何も見ていないはずです。たとえ、裸のView Controllerにはビューがありません。nil ビューはまったく表示されません。次に、この最近提示されたビュー コントローラーが閉じられます。その後、実際のビュー コントローラーがナビゲーション スタックにプッシュされます。

これはコードです:

//set the landscape switch for the tab bar, view controllers and application
[(AppDelegate*) [[UIApplication sharedApplication] delegate] setIsLandscapePreferred:YES];

//set statusbar to the desired rotation position
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft animated:YES];

// Create any view controller. UIViewController will do. 
// Present it modally and directly after that dismiss it again. 
UIViewController *anyVC = [[UIViewController alloc] init];
[self presentModalViewController: anyVC animated:NO];
[self dismissModalViewControllerAnimated:NO];
// The user should not have noticed anything but how the device is in landscape orientation

//Frankly I am not sure whether the next statement must be included or does not need to be. 
//While working on “my” solution I came across a number of tricks and hints on several places on the web and this was amongst them.
//At least it does not do any harm. 
if ([UIViewController respondsToSelector:@selector(attemptRotationToDeviceOrientation)]) {
    // this ensures that the view will be presented in the orientation of the device
    // This method is only supported on iOS 5.0.  iOS 4.3 users may get a little dizzy.
    [UIViewController attemptRotationToDeviceOrientation];
}

// Get the storyboard named secondStoryBoard from the main bundle:
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil];

// I am using storyboard in this project. If you don’t do that then you could just instantiate the view controller the usual way with alloc/init or by loading from nib. 
LandscapeVC *landscapeVC = (landscapeVC *)[storyBoard instantiateViewControllerWithIdentifier:@"LandscapeVC"];


// Then push the new view controller in the usual way:
[self.navigationController pushViewController:paintingVC animated:YES];

横向きのView Controller

もうすぐです。新しいビュー コントローラーLandscapeVCは、タブ バー アプリでランドスケープ モードで表示されるようになりました。LandscapeVC タブ バーのないアプリの場合、ビュー コントローラー自体にいくつかの変更を適用する必要があります。そしてもちろん、ランドスケープ モードのビュー コントローラーを閉じたときに、デバイスが回転して縦向きに戻ることを確認する必要があります。

LandscapeVC.m のスニペット

#pragma mark - actions

// certain tasks need to be performed before the view controller is dismissed. This could be done within the viewWillDisappar. 
// Again, if you do that in viewWillDisappear: then you risk some warnings on the console. I have decided to overlay the “Back” button (leftBarButtonItem) with a custom button that invokes the following action. 
- (IBAction)done :(id)sender{

    //set the landscape switch back to off/NO/portrait
    [(AppDelegate*) [[UIApplication sharedApplication] delegate] setIsLandscapePreferred:NO];

    //set statusbar to the desired rotation position
    [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:YES];

    //present/dismiss viewcontroller in order to activate rotating.
    UIViewController *mVC = [[UIViewController alloc] init];
    [self presentModalViewController:mVC animated:NO];
    [self dismissModalViewControllerAnimated:NO];

    [self.navigationController popViewControllerAnimated:YES];
    return;
}

#pragma mark - Rotation
// Rotation

-(BOOL)hidesBottomBarWhenPushed{
// This one is just for my design. I like to use the full screen of the iPhone for my landscape view controller. 
// In the end it is the size of the pane in my case which motivates me to break with UI guidelines and force the user to use this single view controller in landscape only. 
// Besides this design issue this method is not required for the rotation itself. 
// What it does: It overwrites the getter of the UIViewController property hidesBottomBarWhenPushed. 
// By returning YES as constant the bottom bar (= tab bar in my case) will be hidden. 
// However, it remains hidden in the event that any more view controllers are pushed on top of the navigation stack. 
// So if you plan to drill further down in your navigation from here, it may not be a good idea to hide the bottom bar. 
// You will not get it back until the user returns to the calling view controller. 
    return YES;
}

// Well, if all of your view controllers inherit from MyRotatingViewController or MyRotatingTableViewController
// respectively then the following is redundant. 
// While writing this “tutorial” for stack overflow I noticed that this
// very view controller does not. Don’t ask me why. I may fix it later. 
// However, I want to show to you what is proven to work fine. Therefore I owe you these methods. 
-(BOOL)shouldAutorotate{
    return NO;
}

- (NSInteger)supportedInterfaceOrientations{
    return (UIInterfaceOrientationLandscapeRight | UIInterfaceOrientationLandscapeLeft);
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return (UIInterfaceOrientationLandscapeLeft);
}

それでおしまい。かなり簡単ですね。

于 2013-03-20T16:08:34.263 に答える
1

提示するViewControllerに次のメソッドを記述します。

- (BOOL)shouldAutorotate
{
    AppDelegate *mainDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
    mainDelegate.shouldRotate = YES;
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscapeRight;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return YES;
}

AppDelegate.mにこれを貼り付けます

- (NSUInteger) application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    if(self.shouldRotate)
    {
        self.shouldRotate = NO;
        return UIInterfaceOrientationMaskLandscapeRight;
    }
    return UIInterfaceOrientationMaskPortrait;
}

AppDelegate.hにこれを貼り付けます

@property (assign, nonatomic) BOOL shouldRotate;

ViewWillAppearのpresentingViewControllerに、これを貼り付けます。

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO];
于 2013-03-20T09:30:43.027 に答える