iOS 7: Controlador de exibição de contêiner personalizado com UINavigationController como um controlador de exibição filho

Gostaria de escrever um controlador de exibição de contêiner em tela cheia personalizado com a intenção de colocar um UINavigationController nele como um controlador de exibição filho. A exibição do UINavigationController preencherá a exibição do controlador de exibição de contêiner, para que pareça que o UINavigationController é o controlador de exibição raiz. (Alguém poderia fazer algo assim para, por exemplo, criar a interface do usuário do menu da barra lateral deslizante popularizada pelo Facebook.)

O que eu fiz funciona EXCETO: há uma falha ao apresentar outro controlador de exibição que oculta a barra de status quando o iPhone está na orientação paisagem. Normalmente, a barra de navegação desliza para cima quando a barra de status desaparece e desliza para baixo quando reaparece. Em vez disso, a barra de navegação permanece onde está quando deve deslizar para cima e, quando se supõe que deslize para baixo, é posicionada primeiro com a barra de status sobreposta e depois salta para a posição correta abaixo da barra de status. Basicamente, estou tentando fazer com que o UINavigationController se comporte como se não estivesse dentro de um controlador de exibição de contêiner personalizado.

Abaixo está um código que você pode executar para ver o problema, mas se você não quiser fazer isso, basta dar uma olhada noContainerViewController classe, que implementa um controlador de exibição de contêiner personalizado mínimo.O que está faltando no meu controlador de exibição de contêiner personalizado que está causando esse problema? Funciona quando eu uso um UITabBarController como o controlador de exibição de contêiner, então parece que estou perdendo algo na minha implementação.

MAIS MATERIAL ABAIXO DE LER SE VOCÊ QUER

Se você deseja executar o código de exemplo para ver o problema, aqui está uma visão geral. Existe uma definição de pré-processador chamadaMODO definido emAppDelegate compilar condicionalmente o aplicativo de três maneiras.

QuandoMODE == 1, ViewController está dentro de um UINavigationController. Você pode pressionar um botão "Presente" para apresentarViewControllerWithStatusBarHiddene, em seguida, você pode pressionar um botão "Dispensar" para descartar esse controlador de exibição. Este modo do aplicativo mostra o comportamento que estou procurando.

QuandoMODE == 2, temos a mesma coisa que emMODE == 1 exceto que o UINavigationController está dentro de umContainerViewController. Este modo do aplicativo mostra o comportamento indesejável que tenho atualmente.

QuandoMODO == 3, temos a mesma coisa que emMODE == 1 exceto que o UINavigationController está dentro de um UITabBarController. Este modo do aplicativo mostra que é possível obter o comportamento que estou procurando.

Novamente, para ver o problema, basta pressionar o botão "Apresentar" e depois o botão "Ignorar" quando o iPhone estiver na orientação paisagem.

CÓDIGO

Quatro classes:

ContainerViewControllerAppDelegateViewControllerViewControllerWithStatusBarHidden

ContainerViewController.h

#import <UIKit/UIKit.h>

@interface ContainerViewController : UIViewController

@property (nonatomic) UIViewController * viewController;

@end

ContainerViewController.m

#import "ContainerViewController.h"

// This custom container view controller only has one child view controller,
// whose view fills up the view of the container view controller.
@implementation ContainerViewController

- (UIViewController *)viewController {
    if (self.childViewControllers.count > 0) {
        return [self.childViewControllers firstObject];
    }
    else {
        return nil;
    }
}

- (void)setViewController:(UIViewController *)viewController {
    UIViewController *previousViewController = [self.childViewControllers firstObject];
    if ((previousViewController == nil && viewController != nil)
        || (previousViewController != nil && viewController == nil)
        || (previousViewController != nil && viewController != nil
            && previousViewController != viewController))
    {
        if (previousViewController != nil) {
            // Remove the old child view controller.
            [previousViewController willMoveToParentViewController:nil];
            [previousViewController.view removeFromSuperview];
            [previousViewController removeFromParentViewController];
        }

        if (viewController != nil) {
            // Add the new child view controller.
            [self addChildViewController:viewController];
            self.viewController.view.frame = self.view.bounds;
            self.viewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
            [self.view addSubview:self.viewController.view];
            [self.viewController didMoveToParentViewController:self];
        }
    }
}

- (UIViewController *)childViewControllerForStatusBarHidden {
    return self.viewController;
}

- (UIViewController *)childViewControllerForStatusBarStyle {
    return self.viewController;
}

@end

AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

AppDelegate.m

#import "AppDelegate.h"

#import "ViewController.h"
#import "ContainerViewController.h"

#define MODE 2  // Mode can be 1, 2, or 3.

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    ViewController *vc = [[ViewController alloc] initWithNibName:nil bundle:nil];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];

#if MODE == 1   // No container view controller.

    self.window.rootViewController = nav;

#elif MODE == 2 // Use custom container view controller.

    ContainerViewController *container = [[ContainerViewController alloc] initWithNibName:nil bundle:nil];
    container.viewController = nav;
    self.window.rootViewController = container;

#elif MODE == 3 // Use tab bar controller as container view controller.

    UITabBarController *tab = [[UITabBarController alloc] initWithNibName:nil bundle:nil];
    tab.viewControllers = @[nav];
    self.window.rootViewController = tab;

#endif

    [self.window makeKeyAndVisible];
    return YES;
}

@end

ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

ViewController.m

#import "ViewController.h"

#import "ViewControllerWithStatusBarHidden.h"

// This view controller will serve as the content of a navigation controller.
// It also provides a button in the navigation bar to present another view controller.
@implementation ViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        self.title = @"Title";
        self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Present"
                                                                                  style:UIBarButtonItemStylePlain
                                                                                 target:self
                                                                                 action:@selector(pressedPresentButton:)];
    }
    return self;
}

- (void)loadView {
    self.view = [[UIView alloc] init];
    self.view.backgroundColor = [UIColor whiteColor];
}

- (void)pressedPresentButton:(id)sender {
    ViewControllerWithStatusBarHidden *vc = [[ViewControllerWithStatusBarHidden alloc] initWithNibName:nil bundle:nil];

    [self presentViewController:vc animated:YES completion:nil];
}

@end

ViewControllerWithStatusBarHidden.h

#import <UIKit/UIKit.h>

@interface ViewControllerWithStatusBarHidden : UIViewController

@end

ViewControllerWithStatusBarHidden.m

#import "ViewControllerWithStatusBarHidden.h"

// This view controller is meant to be presented and does two things:
// (1) shows a button to dismiss itself and (2) hides the status bar.
@implementation ViewControllerWithStatusBarHidden

- (void)loadView {
    self.view = [[UIView alloc] init];
    self.view.backgroundColor = [UIColor yellowColor];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [dismissButton setTitle:@"Dismiss" forState:UIControlStateNormal];
    [dismissButton addTarget:self
                      action:@selector(pressedDismissButton:)
            forControlEvents:UIControlEventTouchUpInside];
    dismissButton.frame = CGRectMake(100, 100, 100, 100);

    [self.view addSubview:dismissButton];
}

- (void)pressedDismissButton:(id)sender {
    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}

- (BOOL)prefersStatusBarHidden {
    return YES;
}

//- (NSUInteger)supportedInterfaceOrientations {
//    return UIInterfaceOrientationMaskPortrait;
//}

@end

questionAnswers(0)

yourAnswerToTheQuestion