【问题标题】:iOS/Swift - Hide/Show UITabBarController when scrolling down/upiOS/Swift - 向下/向上滚动时隐藏/显示 UITabBarController
【发布时间】:2015-11-02 21:20:17
【问题描述】:

我对 iOS 开发很陌生。现在,当我向下滚动和向上滚动时,我正试图隐藏我的标签栏。我想让这个动画像导航栏一样。对于导航栏,我只需单击 Attributes Inspector 中的选项。我看到了一些工具栏的例子,但我不能采用它作为标签栏。

self.tabBarController?.tabBar.hidden = true 只是隐藏了我的标签栏,但它不像导航控制器那样具有动画效果。

【问题讨论】:

    标签: ios swift uiscrollview uitabbarcontroller show-hide


    【解决方案1】:

    这是我在生产应用程序中实际使用的代码。

    它在 Swift 中,它还会更新 UITabBar.hidden var。

    func scrollViewWillBeginDragging(scrollView: UIScrollView) {
        if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0{
            changeTabBar(hidden: true, animated: true)
        }
        else{
            changeTabBar(hidden: false, animated: true)
        }
    }
    

    你也可以使用其他回调方法:

    func scrollViewDidScroll(scrollView: UIScrollView) {
        ...
    }
    

    但如果您选择这样做,那么您必须处理对实际隐藏 tabBar 的辅助方法的多次调用。

    然后你需要添加这个方法来动画 tabBar 的隐藏/显示。

    func changeTabBar(hidden:Bool, animated: Bool){
        var tabBar = self.tabBarController?.tabBar
        if tabBar!.hidden == hidden{ return }
        let frame = tabBar?.frame
        let offset = (hidden ? (frame?.size.height)! : -(frame?.size.height)!)
        let duration:NSTimeInterval = (animated ? 0.5 : 0.0)
        tabBar?.hidden = false
        if frame != nil
        {
            UIView.animateWithDuration(duration,
                animations: {tabBar!.frame = CGRectOffset(frame!, 0, offset)},
                completion: {
                    println($0)
                    if $0 {tabBar?.hidden = hidden}
            })
        }
    }
    

    更新 Swift 4

    func changeTabBar(hidden:Bool, animated: Bool){
        guard let tabBar = self.tabBarController?.tabBar else { return; }
        if tabBar.isHidden == hidden{ return }
        let frame = tabBar.frame
        let offset = hidden ? frame.size.height : -frame.size.height
        let duration:TimeInterval = (animated ? 0.5 : 0.0)
        tabBar.isHidden = false
    
        UIView.animate(withDuration: duration, animations: {
            tabBar.frame = frame.offsetBy(dx: 0, dy: offset)
        }, completion: { (true) in
            tabBar.isHidden = hidden
        })
    }
    

    【讨论】:

    • 感谢您的回复,我稍后会在我的项目中尝试实现它
    • 效果很好。这正是我想要的。谢谢。
    • 代码与 tableviewcontroller 一起工作。但是当标签栏隐藏时,标签所在的位置似乎有一个黑色区域。无论如何要解决这个问题还是我错过了什么?
    • 它隐藏了tabBar并显示tabBar下方的视图,它向您显示了一个黑色区域,因为tableViewController没有覆盖该区域。
    • 我将底部约束设置为 Superview 但黑屏没有隐藏。错误在哪里?感谢回复
    【解决方案2】:

    这个答案是对 Ariel 答案的轻微修改,它在用户滚动时添加了动画。

    extension ViewController:UIScrollViewDelegate{
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0{
                //scrolling down
                changeTabBar(hidden: true, animated: true)
            }
            else{
                //scrolling up
                changeTabBar(hidden: false, animated: true)
            }
        }
    
        func changeTabBar(hidden:Bool, animated: Bool){
            let tabBar = self.tabBarController?.tabBar
            let offset = (hidden ? UIScreen.main.bounds.size.height : UIScreen.main.bounds.size.height - (tabBar?.frame.size.height)! )
            if offset == tabBar?.frame.origin.y {return}
            print("changing origin y position")
            let duration:TimeInterval = (animated ? 0.5 : 0.0)
            UIView.animate(withDuration: duration,
                           animations: {tabBar!.frame.origin.y = offset},
                           completion:nil)
        }
    }
    

    【讨论】:

      【解决方案3】:

      基于 Ariel 的回答,我更新了 Swift3 的代码。这对我的收藏视图非常有用。

      override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
              if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0 {
                  changeTabBar(hidden: true, animated: true)
              }else{
                  changeTabBar(hidden: false, animated: true)
              }
      
          }
      
      func changeTabBar(hidden:Bool, animated: Bool){
              let tabBar = self.tabBarController?.tabBar
              if tabBar!.isHidden == hidden{ return }
              let frame = tabBar?.frame
              let offset = (hidden ? (frame?.size.height)! : -(frame?.size.height)!)
              let duration:TimeInterval = (animated ? 0.5 : 0.0)
              tabBar?.isHidden = false
              if frame != nil
              {
                  UIView.animate(withDuration: duration,
                                             animations: {tabBar!.frame = frame!.offsetBy(dx: 0, dy: offset)},
                                             completion: {
                                              print($0)
                                              if $0 {tabBar?.isHidden = hidden}
                  })
              }
          }
      

      【讨论】:

        【解决方案4】:

        您可以通过将您的类设置为 scrollView 的委托并在 scrollViewDidScroll: 方法中实现滚动来精确控制 UITabBar。

        这是我如何在我的应用程序中执行此操作的示例。您可能可以根据需要轻松修改它。包含一些帮助函数来获取 UITabBar。

        #define LIMIT(__VALUE__, __MIN__, __MAX__) MAX(__MIN__, MIN(__MAX__, __VALUE__))
        
        - (void)scrollViewDidScroll:(UIScrollView *)scrollView
        {
            CGFloat scrollOffset = scrollView.contentOffset.y;
            CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
            CGFloat scrollHeight = scrollView.frame.size.height;
            CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;
            CGFloat scrollOffsetGlobal = scrollOffset + scrollView.contentInset.top;
            [self updateUITabBarY:[self UITabBarView].frame.origin.y + scrollDiff];
            self.previousScrollViewYOffset = scrollOffset;
        }
        
        - (UITabBar*) UITabBarView
        {
            for(UIView *view in self.tabBarController.view.subviews)
            {
                if([view isKindOfClass:[UITabBar class]])
                {
                    return (UITabBar*) view;
                }
            }
        
            return nil;
        }
        
        - (void) updateUITabBarY:(CGFloat) y
        {
            UITabBar* tabBar = [self UITabBarView];
            if(tabBar)
            {
                CGRect frame = tabBar.frame;
                frame.origin.y  = LIMIT(y, [self UITabBarMiny], [self UITabBarMaxY]);
                tabBar.frame = frame;
            }
        }
        
        - (CGFloat) UITabBarMiny
        {
            return [UIScreen mainScreen].bounds.size.height - [self UITabBarView].frame.size.height - [[UIApplication sharedApplication] statusBarFrame].size.height + 20.0f;
        }
        
        - (CGFloat) UITabBarMaxY
        {
            return [UIScreen mainScreen].bounds.size.height;
        }
        

        【讨论】:

        • 感谢您的回答。我会先尝试快速回答。
        • 对不起,我为您添加了缺少的宏 - 它只是将第一个参数限制在一定范围内 - (value, MIN, MAX)
        【解决方案5】:

        Ariels 答案有效,但有些值似乎不正确。当您比较 scrollView scrollView.panGestureRecognizer.translation(in: scrollView).y 的 y 值时,“0”具有副作用,即当您停止滚动时 tabBar 显示或隐藏。它使用“0”值再次调用该方法。我用 didEndDragging、didScroll 和 willBeginDragging 尝试了类似的效果。这感觉非常违反直觉或错误。 我在比较 y 值时使用了 +/- 0.1 并获得了所需的效果,它只是在您真正向上或向下滚动时显示和隐藏。

        另一件没有提到的事情是,当应用程序移动到后台时,您使用tabBar.frame = frame.offsetBy(dx: 0, dy: offset) 设置的偏移量将被重置。您向下滚动,tabBar 消失,您更改应用程序,再次打开它,tabBar 仍然隐藏但框架又回到了旧位置。因此,当再次调用该函数时,tabBar 会向上移动更多,并且您会出现 tabBar.frame 大小的间隙。

        为了摆脱这种情况,我比较了当前帧位置并为 alpha 值设置了动画。我无法让通常的恢复动画正常工作,也许有人会尝试,不会那么难。不过这种方式没关系,因为它不经常发生。

        override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        
            let yValue = scrollView.panGestureRecognizer.translation(in: scrollView).y
            if yValue < -0.1 {
                //hide tabBar
                changeTabBar(hidden: true, animated: true)
            } else if yValue > 0.1 {
                //show tabBar
                changeTabBar(hidden: false, animated: true)
            }
        }
        
        func changeTabBar(hidden:Bool, animated: Bool) {
            guard let tabBar = self.tabBarController?.tabBar else {
                return
            }
            if tabBar.isHidden == hidden{
                return
            }
            
            let frame = tabBar.frame
            let frameMinY = frame.minY  //lower end of tabBar
            let offset = hidden ? frame.size.height : -frame.size.height
            let viewHeight = self.view.frame.height
            
            //hidden but moved back up after moving app to background
            if frameMinY < viewHeight && tabBar.isHidden {
                tabBar.alpha = 0
                tabBar.isHidden = false
        
                UIView.animate(withDuration: 0.5) {
                    tabBar.alpha = 1
                }
                
                return
            }
        
            let duration:TimeInterval = (animated ? 0.5 : 0.0)
            tabBar.isHidden = false
        
            UIView.animate(withDuration: duration, animations: {
                tabBar.frame = frame.offsetBy(dx: 0, dy: offset)
            }, completion: { (true) in
                tabBar.isHidden = hidden
            })
        }
        

        【讨论】:

          【解决方案6】:

          根据@Ariel Hernández Amador 对隐藏 Tabbar 后黑屏的回答,只需在 ViewDidLoad() 中使用这行代码即可。工作出色...我在这里发布了这个,因为我无法在那里发表评论。

          viewDidLoad()
          {
          if #available(iOS 11.0, *) {
                      self.myScroll.contentInsetAdjustmentBehavior = .never
                  }
          }
          

          这里的 myScroll 是我在 VC 中使用的 Scrollview。只需将其替换为您的 VC。

          【讨论】:

          • 对我来说,这个解决方案不起作用,但后来我发现了一些约束问题,我已经给出了关于安全区域的底部视图的底部约束,我将其更改为 superView 并且它有效对我来说。(stackoverflow.com/a/55216583/7516788)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-01-17
          • 1970-01-01
          • 2023-01-17
          • 1970-01-01
          • 1970-01-01
          • 2019-06-03
          • 1970-01-01
          相关资源
          最近更新 更多