【问题标题】:View on Top of UITabBar在 UITabBar 顶部查看
【发布时间】:2017-07-12 02:06:45
【问题描述】:

类似于 Spotify 或 Apple Music 应用在播放歌曲时所做的事情,它会在 UITabBar 顶部放置一个自定义视图:

我尝试过的解决方案:

  1. UITabBarController 在 ViewController 中具有最大尺寸的 Container View,以及底部布局指南上方 Container View49pt 顶部的自定义视图: 问题: 嵌入在 UITabBarController 中的 ViewController 中的任何内容都不会显示,因为它们隐藏在自定义布局后面。我尝试在 UITabBarController 中覆盖 size forChildContentContainer,尝试更新底部布局指南,Nothing。 我需要调整UITabBarController的容器视图框架。

  2. 再次尝试#1,但尝试通过increasing the size of UITabBar 解决隐藏在其后面的内容的问题,然后使用ImageInset on every TabBarItem 将其关闭,并将我的自定义视图添加到UITabBar 的顶部。并没有真正奏效。有时我想隐藏我的自定义视图。

  3. UITabBarController 以 root 身份,每个孩子都是一个 ViewController 与一个容器视图 + 我的自定义视图: 但是现在我有多个自定义视图的实例漂浮在周围。如果我想更改其上的标签,则必须将其更改为所有视图。或者隐藏等等。

  4. 覆盖 UITabBarController 的 UITabBar 属性并返回具有 UITabBar + 我的自定义视图的自定义 UITabBar(用 xib 膨胀)。 问题:可能是最令人沮丧的尝试。如果您使用 class MyCustomTabBar : UITabBar {} 的实例覆盖该属性,则不会显示任何选项卡!是的,我将myCustomTabBar 的代表设置为self

倾向于#3,但正在寻找更好的解决方案。

【问题讨论】:

  • #3 绝对是一种解决方法,必须有更好的解决方案。
  • @Azizi Javed 你可以在 UINavigationController 上添加视图
  • @aircraft 你能发布一个你所指的例子吗?因为告诉孩子视图控制器他们的有限框架的问题仍然存在。
  • @sweta.me 我添加了解决方案 #4,但这更糟:\

标签: ios swift uitabbar


【解决方案1】:

如果您继承 UITabBarController 并以编程方式添加您的视图,这实际上非常容易。使用此技术自动支持标签栏的旋转和大小更改,无论您使用的是哪个版本。

class CustomTabBarController: UITabBarController {
  override func viewDidLoad() {
    super.viewDidLoad()

    //...do some of your custom setup work
    // add a container view above the tabBar
    let containerView = UIView()
    containerView.backgroundColor = .red
    view.addSubview(containerView)
    containerView.translatesAutoresizingMaskIntoConstraints = false
    containerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
    containerView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true

    // anchor your view right above the tabBar
    containerView.bottomAnchor.constraint(equalTo: tabBar.topAnchor).isActive = true

    containerView.heightAnchor.constraint(equalToConstant: 50).isActive = true
  }
}

【讨论】:

  • 这不会在标签栏控制器内插入视图控制器,这是重点
  • 要插入视图控制器,您可以这样做:additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 42, right: 0) 在所有视图控制器的基类中
  • additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 42, right: 0) 这在 iOS 14 上不起作用。
【解决方案2】:

我明白了!

本质上,我增加了原始 UITabBar 的大小以容纳自定义视图(并缩小上述视图控制器的框架),然后在其顶部添加一个重复的 UITabBar + 自定义视图。

这是我必须做的事情。我上传了它的功能示例,可以是found in this repo

class TabBarViewController: UITabBarController {

    var currentlyPlaying: CurrentlyPlayingView!
    static let maxHeight = 100
    static let minHeight = 49
    static var tabbarHeight = maxHeight

    override func viewDidLoad() {
        super.viewDidLoad()

        currentlyPlaying = CurrentlyPlayingView(copyFrom: tabBar)
        currentlyPlaying.tabBar.delegate = self

        view.addSubview(currentlyPlaying)
        tabBar.isHidden = true
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        currentlyPlaying.tabBar.items = tabBar.items
        currentlyPlaying.tabBar.selectedItem = tabBar.selectedItem
    }
    func hideCurrentlyPlaying() {
        TabBarViewController.tabbarHeight = TabBarViewController.minHeight
        UIView.animate(withDuration: 0.5, animations: {
            self.currentlyPlaying.hideCustomView()
            self.updateSelectedViewControllerLayout()
        })
    }
    func updateSelectedViewControllerLayout() {
        tabBar.sizeToFit()
        tabBar.sizeToFit()
        currentlyPlaying.sizeToFit()
        view.setNeedsLayout()
        view.layoutIfNeeded()
        viewControllers?[self.selectedIndex].view.setNeedsLayout()
        viewControllers?[self.selectedIndex].view.layoutIfNeeded()
    }
}

extension UITabBar {

    open override func sizeThatFits(_ size: CGSize) -> CGSize {
        var sizeThatFits = super.sizeThatFits(size)
        sizeThatFits.height = CGFloat(TabBarViewController.tabbarHeight)
        return sizeThatFits
    }
}

【讨论】:

  • 不错的解决方案,但是当您旋转设备时会损坏。
  • @Aziz 似乎不工作现在标签栏不显示,github 代码在 iphone X sim 中也不能工作
  • @aziz 如果应用程序在没有 Xcode 的情况下启动,它的行为似乎与调试和工作文件有关
【解决方案3】:

从 iOS 11 开始,这变得更容易了。添加视图时,您可以执行以下操作:

viewControllers?.forEach {
   $0.additionalSafeAreaInsets = UIEdgeInsets(
      top: 0, 
      left: 0,
      bottom: yourView.height,
      right: 0
   )
}

【讨论】:

  • 你的回答真的被低估了恕我直言。如果可以的话,我会完全 +2 :)
  • 这确实是最好的答案。使用此函数为所有 viewController 设置 safeAreaInsets,然后使用 keyWindow = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) 获取所有视图的最顶层视图并添加 yourView keyWindow.addSubview(yourView)。对我来说就像一个魅力。
  • 你是救命稻草!
【解决方案4】:

您将其放入包装器视图控制器的想法很好,但它只会导致开销(更多视图控制器加载到内存中),并且当您稍后想要更改代码时会出现问题。如果您希望该栏始终显示在您的 UITabBarController 上,那么您应该将它添加到那里。

您应该继承UITabBarController 并从笔尖加载自定义栏。在那里您将可以访问标签栏(因此您可以将栏正确放置在其上方),并且您只需加载一次(这解决了您将面临每个标签上都有不同栏的问题)。

至于您的视图对自定义栏的大小没有反应,我不知道您如何做到这一点,但我最好的建议是使用公共变量和您在各个选项卡中收听的通知。 然后,您可以使用它来更改底部约束。

【讨论】:

  • 我可以从 UITabBarController 中的 nib 加载自定义视图,但是我将如何显示它?不知何故让 UITabBarController 显示它? (那将是理想的),还是让每个子视图显示它,如 #3 所示?
  • 子类化 UITabBarController 并从 nib 加载它。然后您可以将其添加到view 并在代码中将其约束到标签栏的顶部。您可以使用固定的 49 像素(标签栏的高度)来限制它,或者使用标签栏的位置。
  • 对了。但是随后子视图控制器延伸到自定义视图下方的问题......我可以在 TabBarController 及其子级之间放置一个容器视图以限制大小,类似于#3,但在 TabBarController 中显示自定义视图。
  • 恐怕你只需要限制你的标签停止在某个高度。另一种选择可能是只创建您自己的具有标签栏、自定义视图和内容容器视图的视图控制器。然后,您可以处理自己的选项卡切换。遗憾的是,这将需要更多的工作,但如果您不想让每个制表位在您的视图之前停止,这就是要走的路。
  • 发布了包含您的答案的解决方案。 :) 让我知道你的想法
【解决方案5】:

除了使用 UITabBar 或容器 vc 之外,您还可以考虑将 App Delegate 中的视图添加到主窗口,如下面的帖子:

View floating above all ViewControllers

由于您的视图与 Tab 栏一起在周围,因此完全可以在 App Delegate 中进行。

您始终可以从 App Delegate Singleton 访问 Floating 视图,方法是将其设为 App Delegate 的属性。这样就很容易控制它在任何代码中的可见性。

改变浮动视图和超级视图窗口之间的Constraints常数可以调整视图的位置,从而很好地响应方向变化。

另一种(类似)方法是使浮动视图成为另一个窗口,如 uid 按钮。

【讨论】:

    【解决方案6】:

    除非我误解了,否则您可以从您的 UITabBarController 类创建自定义视图。然后,您可以将其插入 上方 并将其约束到 tabBar 对象,这是与控制器的 tabBar 关联。

    因此,从您的 UITabBarController 类中,创建您的自定义视图

    class CustomTabBarController: UITabBarController {
        var customView: UIView = {
                let bar = UIView()
                bar.backgroundColor = .white
                bar.translatesAutoresizingMaskIntoConstraints = false
                return bar
            }()
    

    在 viewDidLoad() 中,将您的自定义视图添加到 UITabBarController 的视图对象并将其放置在 上方 tabBar 对象

    override func viewDidLoad() {
            super.viewDidLoad()
    
            ...
    
            self.view.insertSubview(customView, aboveSubview: tabBar)
    

    然后在您的自定义视图添加为子视图后,添加约束以使其正确定位。这也应该在 viewDidLoad() 中完成,但只能在插入视图之后完成。

    self.view.addConstraints([
                NSLayoutConstraint(item: customView, attribute: .leading, relatedBy: .equal, toItem: tabBar, attribute: .leading, multiplier: 1, constant: 0),
                NSLayoutConstraint(item: customView, attribute: .trailing, relatedBy: .equal, toItem: tabBar, attribute: .trailing, multiplier: 1, constant: 0),
                NSLayoutConstraint(item: customView, attribute: .top, relatedBy: .equal, toItem: tabBar, attribute: .top, multiplier: 1, constant: -50),
                NSLayoutConstraint(item: customView, attribute: .bottom, relatedBy: .equal, toItem: tabBar, attribute: .top, multiplier: 1, constant: 0)
                ])
    

    有很多创造性的方法可以设置约束来做你想做的事,但是上面的约束应该在你的 tabBar 上方附加一个高度为 50 的视图。

    【讨论】:

    • 我只发现该代码存在一个问题。当您尝试将新的 viewController 推送到 navigationController 并将 hidesBottomBarWhenPushed 设置为 true 时,您会注意到当您返回之前的 viewController 时,该视图将丢失。
    • 有趣,我从来没有使用或修改过这个属性,所以我从来没有遇到过这个问题。当视图被隐藏时,与视图相关的所有约束都可能会被弄乱(这应该被调查,我只是在猜测)。如果是这种情况,那么每次标签栏重新出现时(从隐藏变为可见)都必须重新应用我上面提到的约束。
    【解决方案7】:
    1. 使视图的框架具有标签栏的高度并将其置于顶部,2. 设置标签栏隐藏为真。

    【讨论】:

      猜你喜欢
      • 2015-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-02
      • 2011-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多