UINavigationController を子ビュー コントローラーとして配置する目的で、カスタム フルスクリーン コンテナー ビュー コントローラーを作成したいと思います。UINavigationController のビューは、UINavigationController がルート ビュー コントローラーのように見えるように、コンテナー ビュー コントローラーのビューを埋めます。(たとえば、Facebook で普及したスライド サイドバー メニュー UI を作成するために、このようなことをしたいと思うでしょう。)
私が行ったことは、iPhoneが横向きのときにステータスバーを非表示にする別のView Controllerを提示するときに不具合があることを除いて機能します。通常、ナビゲーション バーは、ステータス バーが消えると上にスライドし、再表示されると下にスライドします。代わりに、ナビゲーション バーは上にスライドすると想定される場所にとどまり、下にスライドすると想定される場合は、最初にステータス バーが重なるように配置され、次にステータス バーの下の正しい位置にジャンプします。基本的に、UINavigationController がカスタム コンテナー ビュー コントローラー内にない場合と同じように動作するようにしようとしています。
以下は、問題を確認するために実行できるコードですが、実行したくない場合は、最小限のカスタム コンテナー ビュー コントローラーを実装するContainerViewControllerクラスを参照してください。この問題の原因となっているカスタム コンテナー ビュー コントローラーに欠けているものは何ですか? UITabBarController をコンテナー ビュー コントローラーとして使用すると機能するため、実装に何かが欠けているようです。
必要に応じて以下をお読みください
サンプル コードを実行して問題を確認したい場合は、概要を以下に示します。3 つの方法でアプリを条件付きでコンパイルするために、 AppDelegateで定義されたMODEと呼ばれるプリプロセッサ定義があります。
MODE == 1の場合、ViewControllerは UINavigationController 内にあります。次に、「Present」ボタンを押してViewControllerWithStatusBarHiddenを表示し、「Dismiss」ボタンを押してこのビュー コントローラーを閉じることができます。アプリのこのモードは、私が求めている動作を示しています。
MODE == 2の場合、UINavigationController がContainerViewControllerの内部にあることを除いて、 MODE == 1の場合と同じです。アプリのこのモードは、私が現在持っている望ましくない動作を示しています。
MODE == 3の場合、UINavigationController が UITabBarController の内部にあることを除いて、MODE == 1の場合と同じです。アプリのこのモードは、私が求めている動作を取得できることを示しています。
繰り返しになりますが、問題を確認するには、iPhone が横向きのときに [Present] ボタンを押してから [Dismiss] ボタンを押してください。
コード
4 つのクラス:
- ContainerViewController
- AppDelegate
- ViewController
- ViewControllerWithStatusBarHidden
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