iPhone如何在第一页隐藏导航栏?

402
我有下面的代码,它隐藏并显示导航栏。 当第一个视图加载时,它被隐藏,当调用“children”时再次隐藏。 问题是我找不到触发器/事件来让它在返回根视图时再次隐藏....
在根页面上有一个“测试”按钮可以手动执行该操作,但它不够美观,我希望它自动执行。
-(void)hideBar 
{
    self.navController.navigationBarHidden = YES;
}
-(void)showBar 
{       
    self.navController.navigationBarHidden = NO;
}
14个回答

1062

我发现最好的解决方案是在第一个视图控制器中执行以下操作。

Objective-C

- (void)viewWillAppear:(BOOL)animated {
    [self.navigationController setNavigationBarHidden:YES animated:animated];
    [super viewWillAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    [self.navigationController setNavigationBarHidden:NO animated:animated];
    [super viewWillDisappear:animated];
}

Swift

override func viewWillAppear(_ animated: Bool) {
    self.navigationController?.setNavigationBarHidden(true, animated: animated)
    super.viewWillAppear(animated)
}

override func viewWillDisappear(_ animated: Bool) {
    self.navigationController?.setNavigationBarHidden(false, animated: animated)
    super.viewWillDisappear(animated)
} 

当你将下一个UIViewController推入堆栈时,这将导致导航栏从左侧启动动画(与下一个视图一起),并在您按下UINavigationBar上的后退按钮时向左侧消失(与旧视图一起)。

请注意,这些不是委托方法,而是覆盖了UIViewController实现的这些方法,根据文档,您必须在您的实现中某处调用super的实现


2
这太棒了!我至少挣扎了一天。谢谢!!! - James Testa
36
警告:在执行快速返回动作时,会产生一个非常严重的bug。假设A(没有导航栏)和B(有导航栏)被推入堆栈。当在视图B上并执行快速返回动作,但提前释放以停留在B上时,导航栏仍然会被隐藏。现在就无法返回了。这是由于animated=YES导致的。我知道使用animated=NO看起来很丑陋,但似乎当隐藏导航栏的动画还未完成时,再次显示它的动画将被忽略。目前还没有解决办法。 - fabb
3
在Swift中: 覆盖以下函数: 当视图将要出现时: func viewWillAppear(animated: Bool) { self.navigationController?.setNavigationBarHidden(true, animated: true) super.viewWillAppear(true) } 当视图将要消失时: func viewWillDisappear(animated: Bool) { self.navigationController?.setNavigationBarHidden(false, animated: false) super.viewWillDisappear(true) } 这些函数用于控制导航栏的显示和隐藏。 - Kitson
7
问题已于2010年得到解答,并在2015年底对我有所帮助!谢谢。 - oyalhi
1
仍然在2016年坚挺! - JBone
显示剩余9条评论

77

我发现的另一种方法是为 NavigationController 设置委托:

navigationController.delegate = self;

并在 navigationController:willShowViewController:animated: 中使用 setNavigationBarHidden

- (void)navigationController:(UINavigationController *)navigationController 
      willShowViewController:(UIViewController *)viewController 
                    animated:(BOOL)animated 
{   
    // Hide the nav bar if going home.
    BOOL hide = viewController != homeViewController;
    [navigationController setNavigationBarHidden:hide animated:animated];
}

在一个地方轻松地自定义每个ViewController的行为。


这个会在什么时候被调用? - Zalak Patel
2
完美的解决方案。这应该是被接受的答案。谢谢! - Samah
1
完美的答案。即使我们无法在第一个视图控制器上覆盖viewWillAppear和viewWillDisappear方法,它也能正常工作。 - pjuzeliunas
2
太棒了。所选答案可以正常工作,但仅适用于简单的应用程序。当导航栏位于选项卡控制器中并以各种方式推送/呈现各种视图控制器时,此答案可行。 - jontelang
1
这是最好的答案。顶部答案可能会出现错误,如@fabb所述。 - yancaico
显示剩余3条评论

19

在其他答案上我需要稍微调整一下,只有在导航项被推到栏上时,才在viewWillDisappear中取消隐藏栏。这是因为视图可能因其他原因而消失。

因此,只有当此视图不再是最顶层的视图时,我才会取消隐藏栏:

- (void) viewWillDisappear:(BOOL)animated
{
    if (self.navigationController.topViewController != self)
    {
        [self.navigationController setNavigationBarHidden:NO animated:animated];
    }

    [super viewWillDisappear:animated];
}

3
+1,通常在推送模态对话框时不需要显示导航栏。 - João Portela

18

我会将代码放在每个要显示的视图的 viewWillAppear 代理中:

当你需要隐藏它时,就像这样:

- (void)viewWillAppear:(BOOL)animated
{
        [yourObject hideBar];
}

像这样,您需要展示它:

- (void)viewWillAppear:(BOOL)animated
{
        [yourObject showBar];
}

如果这个解决方案解决了你的问题,请把Pablo的回答标记为“解决方案”。 - Rog
2
唯一的问题是,当您从一个视图导航到另一个视图时,导航栏会“弹出”并进入视图。是否可能在第一个视图中没有导航栏,在第二个视图滑入时有导航栏,而不会出现任何弹出? - Henning
2
要使导航栏像您期望的那样滑入/滑出,您需要使用setNavigationBarHidden:animated:。请参见下面的Alan Rogers的答案(实际上应该标记为“解决方案”)。 - Nick Forge
2
这个答案有点错误(viewWill/DidAppear),应该调用super。此外,查看下面的答案,其中提供了一种解决方案,您不需要将其添加到每个视图控制器中。 - Alan Rogers

15

目前被接受的答案与问题中描述的意图不符。问题要求在根视图控制器上隐藏导航栏,但在其他所有地方可见,但被接受的答案在特定的视图控制器上隐藏了导航栏。当第一个视图控制器的另一个实例被推入堆栈时会发生什么?它将隐藏导航栏,即使我们没有查看根视图控制器。

相反,@Chad M.使用UINavigationControllerDelegate策略是不错的,这里是一个更完整的解决方案。步骤:

  1. 子类化UINavigationController
  2. 实现-navigationController:willShowViewController:animated方法,以根据是否显示根视图控制器来显示或隐藏导航栏
  3. 覆盖初始化方法,将UINavigationController子类设置为其自己的委托

这个解决方案的完整代码可以在此Gist中找到。这里是navigationController:willShowViewController:animated的实现:

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    /* Hide navigation bar if root controller */
    if ([viewController isEqual:[self.viewControllers firstObject]]) {
        [self setNavigationBarHidden:YES animated:animated];
    } else {
        [self setNavigationBarHidden:NO animated:animated];
    }
}

2
这个回答比被采纳的更为恰当。 - Pavel Gurov

14

在Swift 3中:

override func viewWillAppear(_ animated: Bool) {
    navigationController?.navigationBar.isHidden = true
    super.viewWillAppear(animated)
}


override func viewWillDisappear(_ animated: Bool) {
    if (navigationController?.topViewController != self) {
        navigationController?.navigationBar.isHidden = false
    }
    super.viewWillDisappear(animated)
}

你能解释一下为什么要检查 != self 吗? - Kitson
2
@Kitson,请查看user486646的答案:我对其他答案进行了一点微调,即仅在viewWillDisappear中取消隐藏栏,如果它消失的原因是由于导航项被推到它上面。这是因为视图可能因其他原因而消失。因此,只有当此视图不再是最顶部的视图时,我才会取消隐藏该栏。 - Eugene Braginets
似乎如果使用 navcontroller.navagationBarHidden,它会破坏整个导航控制器(无法左右滑动)。为了使其正常工作,我改用了 navigationController?.navigationBar.hidden。左右滑动仍然有效,并且不会留下空白空间,因为它似乎在堆栈视图中或其他地方。 - Allison

9

将我的信用归功于@chad-m的回答。

以下是Swift版本:

  1. 创建一个新文件MyNavigationController.swift

import UIKit

class MyNavigationController: UINavigationController, UINavigationControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        self.delegate = self
    }

    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        if viewController == self.viewControllers.first {
            self.setNavigationBarHidden(true, animated: animated)
        } else {
            self.setNavigationBarHidden(false, animated: animated)
        }
    }

}
  1. 在Storyboard中将UINavigationController的类设置为MyNavigationController MyNavigationController 就是这样!

chad-m的答案和我的区别:

  1. 继承自UINavigationController,这样就不会污染你的rootViewController。

  2. 使用self.viewControllers.first而不是homeViewController,这样就不需要在1个Storyboard中的100个UINavigationControllers上执行此操作100次。


认为这是最简洁的答案。谢谢。 - DaSilva

6
在多次尝试后,我终于实现了自己想要的效果。以下是我的尝试: - 我有一个视图,其中包含一张图片,并且我希望图片可以全屏显示。 - 我有一个带有选项卡的导航控制器,因此我需要隐藏它。 - 此外,我的主要要求不仅是隐藏,还需要在显示和隐藏时具有淡入淡出的效果。
以下是我的解决方法:
步骤1 - 当用户点击图片时,我捕获该手势并将其推入新的imageViewController,在imageViewController中,我希望图片可以全屏显示。
- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer {  
NSLog(@"Single tap");
ImageViewController *imageViewController =
[[ImageViewController alloc] initWithNibName:@"ImageViewController" bundle:nil];

godImageViewController.imgName  = // pass the image.
godImageViewController.hidesBottomBarWhenPushed=YES;// This is important to note. 

[self.navigationController pushViewController:godImageViewController animated:YES];
// If I remove the line below, then I get this error. [CALayer retain]: message sent to deallocated instance . 
// [godImageViewController release];
} 

第二步 - 下面所有的步骤都在ImageViewController中进行

第二步.1 - 在ViewDidLoad中,显示导航栏

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
NSLog(@"viewDidLoad");
[[self navigationController] setNavigationBarHidden:NO animated:YES];
}

步骤2.2 - 在viewDidAppear中,设置一个带有延迟的定时器任务(我将其设置为1秒延迟)。在延迟后,加入淡化效果。我使用透明度来实现淡化。

- (void)viewDidAppear:(BOOL)animated
{
NSLog(@"viewDidAppear");

myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self     selector:@selector(fadeScreen) userInfo:nil repeats:NO];
}

- (void)fadeScreen
{
[UIView beginAnimations:nil context:nil]; // begins animation block
[UIView setAnimationDuration:1.95];        // sets animation duration
self.navigationController.navigationBar.alpha = 0.0;       // Fades the alpha channel of   this view to "0.0" over the animationDuration of "0.75" seconds
[UIView commitAnimations];   // commits the animation block.  This Block is done.
}

第2.3步 - 在viewWillAppear下,向图像添加单击手势,并使导航栏半透明。
- (void) viewWillAppear:(BOOL)animated
{

NSLog(@"viewWillAppear");


NSString *path = [[NSBundle mainBundle] pathForResource:self.imgName ofType:@"png"];

UIImage *theImage = [UIImage imageWithContentsOfFile:path];

self.imgView.image = theImage;

// add tap gestures 
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];  
[self.imgView addGestureRecognizer:singleTap];  
[singleTap release];  

// to make the image go full screen
self.navigationController.navigationBar.translucent=YES;
}

- (void)handleTap:(UIGestureRecognizer *)gestureRecognizer 
{ 
 NSLog(@"Handle Single tap");
 [self finishedFading];
  // fade again. You can choose to skip this can add a bool, if you want to fade again when user taps again. 
 myTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self  selector:@selector(fadeScreen) userInfo:nil repeats:NO];
 }

第三步 - 最后在viewWillDisappear中,请确保将所有东西放回原处。
- (void)viewWillDisappear: (BOOL)animated 
{ 
self.hidesBottomBarWhenPushed = NO; 
self.navigationController.navigationBar.translucent=NO;

if (self.navigationController.topViewController != self)
{
    [self.navigationController setNavigationBarHidden:NO animated:animated];
}

[super viewWillDisappear:animated];
}

4

如果仍然有人遇到@fabb在接受的答案中提到的快速后滑取消bug问题,我可以通过覆盖viewDidLayoutSubviews来解决这个问题,除此之外还需要覆盖viewWillAppear/viewWillDisappear,如下所示:

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    self.navigationController?.setNavigationBarHidden(false, animated: animated)
}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    self.navigationController?.setNavigationBarHidden(true, animated: animated)
}

//*** This is required to fix navigation bar forever disappear on fast backswipe bug.
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    self.navigationController?.setNavigationBarHidden(false, animated: false)
}

在我的情况下,我注意到这是因为根视图控制器(隐藏nav的位置)和推送的视图控制器(nav显示)具有不同的状态栏样式(例如深色和浅色)。当你开始向后滑动以弹出视图控制器时,将会有额外的状态栏颜色动画。如果你放开手指以取消交互式弹出,而状态栏动画尚未完成,导航栏就会永远消失!然而,如果两个视图控制器的状态栏样式相同,则不会出现此错误。

1
如果您想在控制器中完全隐藏导航栏,更清晰的解决方案是在根控制器中添加以下内容:
@implementation MainViewController
- (void)viewDidLoad {
    self.navigationController.navigationBarHidden=YES;
    //...extra code on view load  
}

当您在控制器中推送子视图时,导航栏将保持隐藏;如果您想仅在子视图中显示它,则需要在viewWillAppear回调中添加用于显示的代码(self.navigationController.navigationBarHidden=NO;),同样,在viewWillDisappear上添加隐藏它的代码。

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