【问题标题】:How to prevent status bar from overlapping content with hidesBarsOnSwipe set on UINavigationController?如何防止状态栏与 UINavigationController 上设置的 hidesBarsOnSwipe 内容重叠?
【发布时间】:2014-11-10 07:09:16
【问题描述】:

我正在尝试使用 iOS 8 中添加的新功能 - 在用户滚动表格视图时隐藏导航栏(类似于移动 Safari 所做的)。我在viewDidAppearUITableViewController 方法中将UINavigationController 的属性hidesBarsOnSwipe 设置为YES

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    if([self.navigationController respondsToSelector:@selector(hidesBarsOnSwipe)]) {
        self.navigationController.hidesBarsOnSwipe = YES;
    }
}

滚动视图时导航栏隐藏。到目前为止,一切都很好。但是状态栏仍然可见,我的表格视图内容通过它显示出来,看起来很难看:

我尝试将edgesForExtendedLayout 设置为UIEdgeRectNone 或调整表格视图的contentInset,但没有帮助。是否有任何其他解决方案可以将状态栏与导航栏一起隐藏,或使其不透明?

【问题讨论】:

    标签: ios objective-c uinavigationcontroller uinavigationbar ios8


    【解决方案1】:

    其实这很容易做到。您只需要将导航 isNavigationBarHidden 属性与状态栏连接起来。

    Objective-C

    - (BOOL)prefersStatusBarHidden {
        return self.navigationController.isNavigationBarHidden;
    }
    

    斯威夫特

    override func prefersStatusBarHidden() -> Bool {
        return navigationController?.navigationBarHidden ?? false
    }
    

    Swift 3.0

    override var prefersStatusBarHidden: Bool {
        return navigationController?.isNavigationBarHidden ?? false
    }
    

    并确保您的应用程序 .plist 文件中有“查看基于控制器的状态栏外观”=“YES”。

    【讨论】:

    • 这应该被标记为最佳答案,简单而优雅。
    • 这个链接状态栏和导航栏,它们是两个不同的东西
    【解决方案2】:

    根据 anas 的回答,我有一个可行的解决方案(我假设 tableViewController 是您的 UITableViewController 实例):

    UINavigationController 子类中(或者也可能来自tableViewController):

    - (void)viewDidLoad {
        if ([self respondsToSelector:@selector(barHideOnSwipeGestureRecognizer)]) {
            // iOS 8+
            self.hidesBarsOnSwipe = YES;
            [self.barHideOnSwipeGestureRecognizer addTarget:self action:@selector(swipe:)];
        }
    }
    
    - (void)swipe:(UISwipeGestureRecognizer *)recognizer {
        BOOL shouldHideStatusBar = self.navigationController.navigationBar.frame.origin.y < 0;
        tableViewController.hideStatusBar = shouldHideStatusBar;
        [UIView animateWithDuration:0.2 animations:^{
            [tableViewController setNeedsStatusBarAppearanceUpdate];
        }];
    }
    

    在你的tableViewController

    @property(nonatomic, getter = shouldHideStatusBar) BOOL hideStatusBar;
    
    - (BOOL)prefersStatusBarHidden {
        return [self shouldHideStatusBar];
    }
    

    如果这对您不起作用,请告诉我。一些不明显的事情:

    • self.navigationController.navigationBar.frame.origin.y 隐藏时为 -44(导航栏的负高度),可见时为 20(状态栏的高度)。即使在动画期间也没有中间值,因此负值 == 隐藏,非负值 == 可见。
    • 子视图控制器是查询状态栏是否应该隐藏的控制器。就我而言,我在UITabBarController 中的UINavigationController 中有一个UIViewController,直到我覆盖UIViewController 上的prefersStatusBarHidden 后它才起作用。
    • 由于隐藏的状态栏没有框架,除非您将调用 setNeedsStatusBarAppearanceUpdate 包装在动画块中,否则您的内容可能会向上抖动 20 点。
    • 希望语法正确;我从我的 Swift 代码中反向移植了它。

    【讨论】:

    • 是的,这行得通。谢谢!无论如何,由于不同的原因,最终结果仍然令我不满意。要再次显示导航栏(隐藏后),必须快速向下滑动。简单地将表格视图滚动到顶部是行不通的。我认为这可能会让用户感到困惑。也许我做错了什么,或者有人知道如何处理它?
    • 您在谈论点击状态栏滚动到顶部的区域吗?嗯...也许您可以尝试在表格视图上设置UIScrollViewDelegate 并将导航栏动画化回到屏幕上?也许您可以连接到scrollViewDidScrollToTop 并检查导航栏的origin.y?也许?
    • 不,我的意思是常规滚动,但速度较慢(低到不会被识别为“向下滑动”手势)。没关系,我检查了移动 Safari,它的工作原理是一样的。所以它应该以这种方式工作,而不是我的预期。
    • 是的,这是我的另一个想法,我想这只是一个新的约定,还没有人真正习惯,因为周围没有很多 iOS 8 应用程序!
    • 我刚刚发布了这个小类,它非常模仿 Safari 的滑动隐藏行为github.com/appfigures/AFSwipeToHide
    【解决方案3】:

    这个新属性带有它的barHideOnSwipeGestureRecognizer

    来自UINavigationController Class Reference

    您可以根据需要对手势识别器进行更改,但不得 更改其委托,您不得删除默认目标对象 以及随之配置的操作。不要试图替换这个 通过覆盖属性来识别手势。

    但你可以添加一个目标:

    [self.navigationController setHidesBarsOnSwipe:YES];
    [self.navigationController.barHideOnSwipeGestureRecognizer addTarget:self action:@selector(swipeGesture:)];
    

    ...并在回调中做任何你想做的事情:

    - (void)swipeGesture:(UIPanGestureRecognizer*)gesture
    {
        // Tweak the status bar
    }
    

    您可能需要手动打开手势状态,确定何时隐藏/显示状态栏等。希望对您有所帮助!

    【讨论】:

    • 你知道为什么我在离开视图控制器并再次加载后尝试滑动时会崩溃吗?它第一次工作,但当我再次尝试时出现错误的访问错误。
    • @pasevin 很难说没有看到崩溃日志
    • 这种做法太棒了。但是我找不到方法来知道手势是 显示 还是 隐藏 导航栏,知道如何在 UIPanGestureRecognizer 中找到这些信息吗?
    【解决方案4】:

    @iOSergey 的回答非常完美!

    这是 Swift 1.2 中的解决方案。将以下代码添加到视图 .swift 文件中:

    override func prefersStatusBarHidden() -> Bool {
    
        return self.navigationController!.navigationBarHidden as Bool
    
    }
    

    【讨论】:

      【解决方案5】:

      如果你想用动画隐藏状态栏:

      override func preferredStatusBarUpdateAnimation() -> UIStatusBarAnimation {
          return .Slide
      }
      
      override func prefersStatusBarHidden() -> Bool {
          return navigationController?.navigationBarHidden ?? false
      }
      

      【讨论】:

        【解决方案6】:

        经过一番挣扎,我终于能够解决这个问题。

        1. 将 TableViewController 更改为 UIViewController
        2. 将一个 TableView 拖到 UIViewController 中,并在 main.storyboard 中的导航栏正下方对齐
        3. 添加缺少的约束。
        4. 点击 tableview 并检查约束
        5. 将底部空间设置为:superview 为 0
        6. 将超级视图的尾随空格设置为 0
        7. 将 superview 的前导空间设置为 0
        8. 设置“顶部对齐:顶部布局指南。顶部为 0(非常重要)

        在 UIViewController 的 viewDidLoad 函数中添加如下代码:

        // 为状态栏创建纯色背景(在 Swift 代码中)

        让 statusFrame = CGRectMake(0.0, 0, self.view.bounds.size.width,
        UIApplication.sharedApplication().statusBarFrame.size.height)

        var statusBar = UIView(frame: statusFrame)

        statusBar.backgroundColor = UIColor.whiteColor()

        self.view.addSubview(statusBar)

        您正在做的是在导航栏正下方创建一个实心栏。随着导航栏向上移动,实心栏也向上移动并位于状态栏的正后方。

        唯一的问题是,当您将屏幕横向旋转时,即使状态栏消失,白条仍然存在。

        【讨论】:

          【解决方案7】:

          好吧,我花了一整天的时间做这个,希望这可以帮助一些人。有一个barHideOnSwipeGestureRecognizer。因此,您可以为相应的UIPanGesture 创建一个侦听器,注意如果导航栏被隐藏,则它的 y 原点为 -44.0;否则为 0(不是 20,因为我们隐藏了状态栏!)。

          在您的视图控制器中(swift 2):

           // Declare at beginning
          var curFramePosition: Double!
          var showStatusBar: Bool = true
          self.navigationController?.barHideOnSwipeGestureRecognizer.addTarget(self, action: "didSwipe:")
          
          ...
          
          override func viewDidLoad(){
              self.navigationController?.hidesBarsOnSwipe = true
            curFramePosition = 0.0 // Not hidden
            self.navigationController?.barHideOnSwipeGestureRecognizer.addTarget(self, action: "didSwipe:")
            ...
          }
          
          func didSwipe(swipe: UIPanGestureRecognizer){
              // Visible to hidden
              if curFramePosition == 0 && self.navigationController?.navigationBar.frame.origin.y == -44 {
                  curFramePosition = -44
                  showStatusBar = false
                  prefersStatusBarHidden()
                  setNeedsStatusBarAppearanceUpdate()
              }
              // Hidden to visible
              else if curFramePosition == -44 && self.navigationController?.navigationBar.frame.origin.y == 0 {
                  curFramePosition = 0
                  showStatusBar = true
                  prefersStatusBarHidden()
                  setNeedsStatusBarAppearanceUpdate()
              }
          }
          
          override func prefersStatusBarHidden() -> Bool {
              if showStatusBar{
                  return false
              }
              return true
          }
          

          【讨论】:

            【解决方案8】:

            另一种方法是添加另一个视图(在 tableview 或 collectionview 或 webview 或 scrollview 或任何其他视图之上)并将视图的顶部约束设置为“Superview.Top”并将其底部约束设置为“Top Layout Guide.Bottom” ,设置视图的背景颜色 就是这样,您甚至可以在 Interface Builder 中完成所有操作而无需任何代码。 如果你想响应那个事件,你可以添加一个 keypath 观察者到视图的 bounds change ,或者子类化视图并覆​​盖它的 bounds setter...

            【讨论】:

              【解决方案9】:

              您需要将导航 isNavigationBarHidden 属性与状态栏连接起来。

                  return self.navigationController.isNavigationBarHidden;
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2017-07-24
                • 1970-01-01
                • 2012-08-27
                • 2016-01-01
                • 1970-01-01
                • 2017-03-10
                • 1970-01-01
                • 2010-10-01
                相关资源
                最近更新 更多