将子视图控制器添加到UINavigationController

20
我正在尝试使用以下代码将一个子视图控制器添加到包含在UINavigationController中的UIViewController中:
- (void)buttonTapped:(id)sender
{
    MyChildController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:@"MyChild"];
    [self addChildViewController:viewController];
    [self.view addSubview:viewController.view];
    [viewController didMoveToParentViewController:self];


    viewController.view.alpha = 0.0f;
    [UIView animateWithDuration:0.4 animations:^{
        viewController.view.alpha = 1.0f;
    }];
}

但这是结果:

图片结果

如您所见,UINavigatioBarUIToolbar仍然在子视图控制器的顶部。我该如何将子视图控制器置于所有内容之上?我已经尝试使用以下代码进行替换:

[self.navigationController addChildViewController:viewController];
    [self.navigationController.view addSubview:viewController.view];
    [viewController didMoveToParentViewController:self.navigationController];

但是这样一来,viewControllerviewDidAppear:animated方法不会被调用。我不知道为什么。

3个回答

34

@Sam 的评论是正确的。你需要调用 beginApperanceTransition:animated:endAppearanceTransition 方法才能触发 viewDidAppear。当你添加子视图控制器时,UINavigationController 之所以不调用 viewDidAppear 是因为它覆盖了其容器组成方法,以防止程序员在奇怪的位置添加子视图控制器。在你的情况下,它不希望你的子视图控制器覆盖导航栏。正确使用导航控制器的方式是让子视图出现在导航栏下方。尽管如此,你仍然可以通过手动告诉子视图何时出现和何时完成出现来强制使用这种非标准 UI。

将一个子视图添加到 UINavigationController

MyChildViewController* child = [[MyChildViewController alloc] init];
[self.navigationController addChildViewController:child];
child.view.frame = self.navigationController.view.bounds;
[self.navigationController.view addSubview:child.view];
child.view.alpha = 0.0;
[child beginAppearanceTransition:YES animated:YES];
[UIView
    animateWithDuration:0.3
    delay:0.0
    options:UIViewAnimationOptionCurveEaseOut
    animations:^(void){
        child.view.alpha = 1.0;
    }
    completion:^(BOOL finished) {
        [child endAppearanceTransition];
        [child didMoveToParentViewController:self.navigationController];
    }
];

从UINavigationController中删除一个子视图控制器

[child willMoveToParentViewController:nil];
[child beginAppearanceTransition:NO animated:YES];
[UIView
    animateWithDuration:0.3
    delay:0.0
     options:UIViewAnimationOptionCurveEaseOut
    animations:^(void){
        child.view.alpha = 0.0;
    }
    completion:^(BOOL finished) {
        [child endAppearanceTransition];
        [child.view removeFromSuperview];
        [child removeFromParentViewController];
    }
];

1
我认为当子视图控制器被移除时,应该调用didMoveToParentViewController:方法。类似这样,[child didMoveToParentViewController:nil] - Mohammad Abdurraafay
2
应该在 addSubview 之前调用 beginAppearanceTransition,并在 removeFromSuperview 之后调用 endAppearanceTransition,否则可能会出现“不平衡的调用”错误。 - thijsonline

8
在您的第一个视图控制器中,可以这样做:
- (IBAction)buttonClick:(id)sender
{
    SecondViewController *secondView = [self.storyboard instantiateViewControllerWithIdentifier:@"SecondViewController"];
    UIImage *blurryImage = [UIImage imageNamed:@"foo.jpeg"];
    secondView.imageView.image = blurryImage;
    [self.navigationController addChildViewController:secondView];
    secondView.view.frame = self.navigationController.view.frame;
    [self.navigationController.view addSubview:secondView.view];
}

然后在您的第二个视图控制器中,添加您的ImageView的getter方法:

-(UIImageView *)imageView
{
    if( _imageView == nil )
    {
        _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 548)];
        [self.view addSubview:_imageView];
    }
    return _imageView;
}

我正在尝试使用视图控制器容器方法,因为我需要模态视图控制器的背景视图模糊且半透明。效果与从屏幕顶部下拉的通知视图相同。背景是半透明的,而且也是模糊/磨砂玻璃。为什么如果我将一个子视图控制器添加到 self.navigationController 中,viewDidAppear 方法不会被调用? - Fred Collins
嗯...不太确定...你试过调用[viewController viewWillAppear:NO]吗? - JonahGabriel
1
是的,它什么也不做。我需要在[viewController didMoveToParentViewController:self.navigationController]这行代码后立即调用[viewController viewDidAppear:NO]方法。为什么这些方法没有被调用?否则,如果我将子视图控制器添加到self而不是self.navigationController,它可以正常工作,但是如上图所示,该视图位于navigationController内部。 - Fred Collins
所以我刚刚编写了一个简单的案例来解决你的问题,看起来viewDidLoad被调用了。为什么不把你的逻辑放在那里呢?我想象一下,当视图隐藏时,你正在释放你的视图,对吗? - JonahGabriel
4
为了使 viewWillAppear:viewDidAppear: 方法被调用,您需要向负责添加视图的视图控制器发送消息 -beginAppearanceTransition:animated:。然后执行动画(如果有的话)。在动画完成块中,向出现的视图控制器发送 -endAppearanceTransition。注意:不要调用 [viewController viewDidAppear:NO] - 您不应该自己调用这些方法。 - Sam
显示剩余3条评论

3

@Pwner的答案,Swift版本:

将子视图添加到UINavigationController

let child = MyChildViewController()
self.navigationController?.addChildViewController(child)
guard let navigationController = navigationController else {
    return
}
child.view.frame = navigationController.view.bounds
child.beginAppearanceTransition(true, animated: true)
self.navigationController?.view.addSubview(child.view)
self.view.alpha = 0
UIView.animate(withDuration: 0.3, animations: {
    child.view.alpha = 1.0
}, completion: { _ in
    guard let navigationController = self.navigationController else {
        return
    }
    child.endAppearanceTransition()
    child.didMove(toParentViewController: navigationController)
})

从UINavigationController中移除一个子控制器

child.willMove(toParentViewController: nil)
child.beginAppearanceTransition(false, animated: true)
UIView.animate(withDuration: 0.3, animations: {
    child.view.alpha = 0.0
}, completion: { _ in
    guard let navigationController = self.navigationController else {
        return
    }
    child.view.removeFromSuperview()
    child.endAppearanceTransition()
    child.removeFromParentViewController()
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接