4

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 つのクラス:

  1. ContainerViewController
  2. AppDelegate
  3. ViewController
  4. 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
4

0 に答える 0