iPhone仅在第一页隐藏导航栏

Posted

技术标签:

【中文标题】iPhone仅在第一页隐藏导航栏【英文标题】:iPhone hide Navigation Bar only on first page 【发布时间】:2021-07-21 09:59:14 【问题描述】:

我有下面的代码隐藏和显示导航栏。它在第一个视图加载时隐藏,然后在调用“孩子”时隐藏。麻烦的是,当他们返回根视图时,我找不到触发它再次隐藏的事件/动作....

我在根页面上有一个手动执行操作的“测试”按钮,但它并不漂亮,我希望它是自动的。

-(void)hideBar 

    self.navController.navigationBarHidden = YES;

-(void)showBar 
       
    self.navController.navigationBarHidden = NO;

【问题讨论】:

【参考方案1】:

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

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];

斯威夫特

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 对这些方法的实现,并且根据文档,您必须在您的实现中的某处调用超级实现

【讨论】:

这太棒了!我为此苦苦挣扎了至少一天。谢谢!!! 警告: 这会在快速向后滑动时产生一个非常糟糕的错误。假设 A(无导航栏)和 B(有导航栏)被推入堆栈。当在视图 B 上并快速向后滑动时,但提前释放以留在 B 上时,导航栏仍然被隐藏。现在已经没有办法回去了。这是由于animated=YES。我知道animated=NO 看起来很难看,但似乎当隐藏导航栏的动画尚未完成时,再次显示它的动画将被忽略。还没有解决方案。 在 Swift 中:覆盖 func viewWillAppear(animated: Bool) self.navigationController?.setNavigationBarHidden(true, animated: true) super.viewWillAppear(true) override func viewWillDisappear(animated: Bool) self .navigationController?.setNavigationBarHidden(false, animated: false) super.viewWillDisappear(true) 问题在 2010 年得到了回答,并在 2015 年底帮助了我!谢谢。 现在这就是我所说的传奇答案。绝妙的把戏伙伴。即使在几十年后工作......也迅速实施了同样的工作,完美无瑕。为您的回答 +1 @Alan Rogers【参考方案2】:

我发现的另一种方法是为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 自定义行为的简单方法。

【讨论】:

什么时候调用? 完美解决方案。这应该是公认的答案。谢谢! 完美答案。如果我们无法在第一个视图控制器上覆盖 viewWillAppear 和 viewWillDisappear 方法,它也可以工作。 太棒了。选择的答案可以正常工作,但仅适用于简单的应用程序。当导航栏位于选项卡控制器中并以各种方式推送/呈现各种 VC 时,此答案有效。 这是最好的答案。上面的答案可能会出现@fabb 的描述的错误。【参考方案3】:

我必须对其他答案进行的一个小调整是仅在 viewWillDisappear 中取消隐藏该栏的原因是它消失的原因是由于导航项被推到它上面。这是因为视图可能因其他原因而消失。

因此,如果此视图不再是最顶层视图,我只会取消隐藏栏:

- (void) viewWillDisappear:(BOOL)animated

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

    [super viewWillDisappear:animated];

【讨论】:

+1,您通常不想在推送模式对话框时显示导航栏。【参考方案4】:

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

像这样你需要隐藏它:

- (void)viewWillAppear:(BOOL)animated

        [yourObject hideBar];

像这样你需要展示的地方:

- (void)viewWillAppear:(BOOL)animated

        [yourObject showBar];

【讨论】:

Lee,如果这解决了您的问题,请将 Pablo's 标记为“解决方案”答案。 唯一的问题是当您从一个视图导航到下一个视图时,导航栏会“弹出”并进入视图。是否有可能只是在第一个视图上没有导航栏,当第二个视图滑入到位时,它有导航栏,没有任何弹出? @henning 要使 NavBar 像您期望的那样滑入/滑出,您需要使用 setNavigationBarHidden:animated:。请参阅下面 Alan Rogers 的回答(应该真正标记为“解决方案”)。 这个答案有点错误(viewWill/DidAppear)应该叫super。另请参阅下面的我的答案,以获得不需要将其添加到每个视图控制器的解决方案。【参考方案5】:

当前接受的答案与问题中描述的预期行为不匹配。该问题要求将导航栏隐藏在根视图控制器上,但在其他任何地方都可见,但接受的答案会隐藏特定视图控制器上的导航栏。当第一个视图控制器的另一个实例被压入堆栈时会发生什么?即使我们没有查看根视图控制器,它也会隐藏导航栏。

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

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

此解决方案的完整代码可在this 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];
    

【讨论】:

这是一个比公认的更合适的答案【参考方案6】:

在 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,检查 user486646 的答案:我必须对其他答案进行的一个小调整是仅在 viewWillDisappear 中取消隐藏栏,如果它消失的原因是由于导航项目被推上它。这是因为视图可能因其他原因而消失。因此,如果此视图不再是最顶层视图,我只会取消隐藏栏 看来如果你使用navcontroller.navagationBarHidden 它会破坏整个导航控制器(不能来回滑动)。为了让它工作,我改用navigationController?.navigationBar.hidden。滑动仍然有效,并且不会留下空白空间,因为它似乎在堆栈视图或其他东西内【参考方案7】:

感谢 @chad-m 的回答。

这里是 Swift 版本:

    创建一个新文件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)
        
    


    将 StoryBoard 中的 UINavigationController 类设置为 MyNavigationController 就是这样!

chad-m 的回答和我的不同:

    继承自 UINavigationController,因此您不会污染您的 rootViewController。

    使用 self.viewControllers.first 而不是 homeViewController,因此您不会在 1 个 StoryBoard 中为 100 个 UINavigationController 执行此操作 100 次。

【讨论】:

认为这是最干净的答案。谢谢【参考方案8】:

经过多次试验,这就是我如何让它达到我想要的效果。 这就是我正在尝试的。 - 我有一个图像视图。我想让图像全屏显示。 - 我也有一个带有 tabBar 的导航控制器。所以我也需要隐藏它。 - 另外,我的主要要求不仅仅是隐藏,而且在显示和隐藏时也要有淡化效果。

这就是我让它工作的方式。

第 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];
 

第 2 步 - 以下所有这些步骤都在 ImageViewController 中

步骤 2.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 秒延迟)。并在延迟后添加淡入淡出效果。我正在使用 alpha 来使用淡入淡出。

- (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];
 

第 3 步 - 最后在 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];

【讨论】:

【参考方案9】:

如果有人仍然遇到@fabb 在接受的答案中评论的快速后退取消错误的问题。

我设法通过覆盖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)

在我的例子中,我注意到这是因为根视图控制器(隐藏导航)和推送视图控制器(显示导航)具有不同的状态栏样式(例如深色和浅色)。当您开始向后滑动以弹出视图控制器的那一刻,将会有额外的状态栏颜色动画。如果您松开手指以取消交互式弹出,在状态栏动画未完成时,导航栏将永远消失!

但是,如果两个视图控制器的状态栏样式相同,则不会出现此错误。

【讨论】:

【参考方案10】:

如果您想要在控制器中完全隐藏导航栏,一个更简洁的解决方案是,在根控制器中,有类似的东西:

@implementation MainViewController
- (void)viewDidLoad 
    self.navigationController.navigationBarHidden=YES;
    //...extra code on view load  

当您在控制器中推送子视图时,导航栏将保持隐藏状态;如果你想只在孩子中显示它,你将在viewWillAppear回调中添加显示it(self.navigationController.navigationBarHidden=NO;)的代码,类似地在viewWillDisappear上隐藏它的代码

【讨论】:

【参考方案11】:

最简单的实现可能是让每个视图控制器在其viewWillAppear:animated: 方法中指定其导航栏是否隐藏。同样的方法也适用于隐藏/显示工具栏:

- (void)viewWillAppear:(BOOL)animated 
    [self.navigationController setToolbarHidden:YES/NO animated:animated];
    [super viewWillAppear:animated];

【讨论】:

实际上,我的建议只对工具栏有意义,因为在没有匹配调用的情况下隐藏导航栏会导致用户无法从当前视图返回。【参考方案12】:

只在首页隐藏导航栏也可以通过storyboard实现。在情节提要上,转到 Navigation Controller Scene->Navigation Bar。并从 属性检查器 中选择“隐藏”属性。这将从第一个视图控制器开始隐藏导航栏,直到它对所需的视图控制器可见。

导航栏可以在 ViewController 的 ViewWillAppear 回调中重新设置为可见。

-(void)viewWillAppear:(BOOL)animated 

    [self.navigationController setNavigationBarHidden:YES animated:animated];
    [super viewWillAppear:animated];                                                  

【讨论】:

【参考方案13】:

斯威夫特 4:

在要隐藏导航栏的视图控制器中。

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)

【讨论】:

【参考方案14】:

通过在您的 ViewController 中实现此代码,您可以获得此效果 其实诀窍是,在启动控制器时隐藏导航栏

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

当用户离开该页面时取消隐藏导航栏是 viewWillDisappear

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

【讨论】:

以上是关于iPhone仅在第一页隐藏导航栏的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript - Bootstrap 4 - 仅在折叠时隐藏导航栏

导航栏仅在 iOS5 中部分隐藏

iPhone X - 隐藏状态栏向上推动导航栏

导航栏在第二次和后续出现时隐藏在视图控制器上

iOS - 仅在一个视图上隐藏导航栏?

在基于导航的应用程序-iphone中的选定视图中隐藏导航栏