29

我在 iPhone X 模拟器上测试最新的 iOS 11 时遇到了一个烦人的问题。

我有一个UITabBarController,每个选项卡内都有一个UINavigationController,每个选项卡UINavigationBar还定义了一个底部工具栏(setToolbarHidden:),默认情况下它们显示在底部,就在 tabBar 上方。

到目前为止,它运行良好,而且似乎在即将推出的 iPhone 8 和 8 Plus 型号中也运行良好,但在 iPhone X 上,工具栏和标签栏之间存在差距。我的猜测是 toolBar 没有意识到它显示在 tabBar 内,然后将容纳空间留在底部。

我想修复它的唯一方法是使用自定义工具栏并自己显示/动画它而不是使用默认值UINavigationBar,但我想听听其他选项:)

  • 这就是它在 iPhone 8 上的外观。
  • 这是 iPhone X 上的问题。

在此处输入图像描述 在此处输入图像描述

4

5 回答 5

4

我将其归档为 radr://problem/34421298,它作为 radr://problem/34462371 的副本被关闭。但是,在带有 iOS 11.2 的 Xcode 9.2 (9C32c) 的最新测试版中,这似乎已得到修复。这是我的应用程序在每个设备的模拟器中运行的示例,两者之间没有任何变化。

iOS 11.1 和 11.2 下的导航栏工具栏

这并不是您问题的真正解决方案,除了一些耐心可以解决它而无需诉诸 UI 诡计。我的假设是 iOS 11.2 将在今年年底之前发布,因为它需要支持 HomePod。

于 2017-11-06T03:09:11.010 回答
1

iOS 11.1 和 iPhone X 已发布,但此错误/功能尚未修复。所以我实施了这个解决方法。此代码适用于 iOS 9.0+。

只需在情节提要中将此类设置为导航控制器的类即可。它将在 iPhone X 中使用具有正确布局约束的自定义工具栏,并在其他设备中回退到本机工具栏。自定义工具栏被添加到导航控制器的视图而不是视图控制器中,以使过渡更平滑。

  • 重要提示:您必须updateItems(animated:)在设置视图控制器后手动调用toolbarItems以更新界面。如果你设置toolbarItems了导航控制器的属性,你可以忽略这一步。

它模拟所有本机工具栏行为(包括在纵向/横向模式下更改工具栏高度),除了推/弹出动画。

import UIKit

class FixNavigationController: UINavigationController {

    private weak var alterToolbarHeightConstraint: NSLayoutConstraint?

    private var _alterToolbar: UIToolbar?

    private func initAlretToolbar() {
        _alterToolbar = UIToolbar()
        _alterToolbar!.isTranslucent = true
        _alterToolbar!.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(_alterToolbar!)
        if view.traitCollection.verticalSizeClass == .compact {
            alterToolbarHeightConstraint = _alterToolbar!.heightAnchor.constraint(equalToConstant: 32.0)
        } else {
            alterToolbarHeightConstraint = _alterToolbar!.heightAnchor.constraint(equalToConstant: 44.0)
        }
        let bottomAnchor: NSLayoutConstraint
        if #available(iOS 11.0, *) {
            bottomAnchor = _alterToolbar!.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
        } else {
            bottomAnchor = _alterToolbar!.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor)
        }
        NSLayoutConstraint.activate([
            _alterToolbar!.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            _alterToolbar!.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            bottomAnchor,
            alterToolbarHeightConstraint!
            ])
        self.view.updateFocusIfNeeded()
        self.view.layoutIfNeeded()
    }

    private var alterToolbarInSuper: UIToolbar? {
        var superNavigationController = self.navigationController as? FixNavigationController
        while superNavigationController != nil {
            if superNavigationController?._alterToolbar != nil {
                return superNavigationController?._alterToolbar
            }
            superNavigationController = superNavigationController?.navigationController as? FixNavigationController
        }
        return nil
    }

    private var alterToolbar: UIToolbar! {
        get {
            if let t = alterToolbarInSuper {
                return t
            }
            if _alterToolbar == nil {
                initAlretToolbar()
            }
            return _alterToolbar
        }
    }

    // This is the logic to determine should use custom toolbar or fallback to native one
    private var shouldUseAlterToolbar: Bool {
        // return true if height is iPhone X's one
        return UIScreen.main.nativeBounds.height == 2436
    }

    /// Manually call it after setting toolbar items in child view controllers
    func updateItems(animated: Bool = false) {
        if shouldUseAlterToolbar {
            (_alterToolbar ?? alterToolbarInSuper)?.setItems(viewControllers.last?.toolbarItems ?? toolbarItems, animated: animated)
        }
    }

    override var isToolbarHidden: Bool {
        get {
            if shouldUseAlterToolbar {
                return _alterToolbar == nil && alterToolbarInSuper == nil
            } else {
                return super.isToolbarHidden
            }
        }
        set {
            if shouldUseAlterToolbar {
                if newValue {
                    super.isToolbarHidden = newValue
                    _alterToolbar?.removeFromSuperview()
                    _alterToolbar = nil
                    self.view.updateFocusIfNeeded()
                    self.view.layoutIfNeeded()
                    // TODO: Animation when push/pop
                    alterToolbarHeightConstraint = nil
                    var superNavigationController = self.navigationController as? FixNavigationController
                    while let superNC = superNavigationController {
                        if superNC._alterToolbar != nil {
                            superNC._alterToolbar?.removeFromSuperview()
                            superNC._alterToolbar = nil
                            superNC.view.updateFocusIfNeeded()
                            superNC.view.layoutIfNeeded()
                        }
                        superNavigationController = superNC.navigationController as? FixNavigationController
                    }
                } else {
                    alterToolbar.setItems(viewControllers.last?.toolbarItems ?? toolbarItems, animated: false)
                }
            } else {
                super.isToolbarHidden = newValue
            }
        }
    }

    override func setToolbarItems(_ toolbarItems: [UIBarButtonItem]?, animated: Bool) {
        super.setToolbarItems(toolbarItems, animated: animated)
        updateItems(animated: animated)
    }

    override var toolbarItems: [UIBarButtonItem]? {
        get {
            return super.toolbarItems
        }
        set {
            super.toolbarItems = newValue
            updateItems()
        }
    }

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        guard let _alterToolbar = _alterToolbar else {
            return
        }
        self.alterToolbarHeightConstraint?.isActive = false
        let height: CGFloat = (view.traitCollection.verticalSizeClass == .compact) ? 32.0 : 44.0
        let alterToolbarHeightConstraint = _alterToolbar.heightAnchor.constraint(equalToConstant: height)
        alterToolbarHeightConstraint.isActive = true
        self.alterToolbarHeightConstraint = alterToolbarHeightConstraint
    }
}
于 2017-11-01T19:22:00.883 回答
1

如果您不考虑旋转,您可以尝试将工具栏的图层作为一种非常简单但快速的解决方法来操作。

class FixNavigationController: UINavigationController
{
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        updateTollbarPosition()
    }

    func updateTollbarPosition() {
        guard let tabbarFrame = tabBarController?.tabBar.frame else {
            return
        }
        let gapHeight = tabbarFrame.origin.y-toolbar.frame.origin.y-toolbar.frame.size.height

        var
        frame = toolbar.layer.frame
        frame.origin.y += gapHeight

        toolbar.layer.frame = frame
    }    
}

不幸的是,当涉及到这种方法时,旋转动画看起来并不好。在这种情况下,添加自定义工具栏而不是标准工具栏将是更好的解决方案。

于 2017-10-12T12:22:03.647 回答
1

我只找到了一种解决方法:将工具栏直接添加到视图控制器

在此处输入图像描述

于 2017-10-18T03:19:23.357 回答
0

Apple 尚未在 iOS 11.2 中修复此错误。源自 Mousavian 的解决方案,这是我采用的一种更简单的方法。

我采用这种方法是因为我只有一个 UITableViewController 发生此错误。因此,就我而言,我只是将下面列出的以下代码添加到发生此错误的 ViewController(即 UITableViewController)中。

优点是:

  • 此修复仅在 iPhone X 的情况下接管。在其他设备上没有副作用
  • 适用于任何过渡
  • 无论其他父/子控制器是否有工具栏都可以工作
  • 简单的

这是代码:

1.将 startFixIPhoneXToolbarBug添加到您的viewWillAppear中,如下所示:

override func viewWillAppear(_ animated: Bool)
{
    super.viewWillAppear(animated)

    startFixIPhoneXToolbarBug()
}

2.将 endFixIPhoneXToolbarBug 添加到您的viewWillDisappear中,如下所示:

override func viewWillDisappear(_ animated: Bool)
{
    super.viewWillDisappear(animated)

    endFixIPhoneXToolbarBug()
}

3.Implement start/endFixIPhoneXToolbarBug在你的viewController像这样:

private var alterToolbarHeightConstraint: NSLayoutConstraint? = nil
private var alterToolbar: UIToolbar? = nil

func startFixIPhoneXToolbarBug()
{
    // Check if we are running on an iPhone X
    if UIScreen.main.nativeBounds.height != 2436
    {
        return  // No
    }
    // See if we have a Toolbar
    if let tb:UIToolbar = self.navigationController?.toolbar
    {
        // See if we already added our own
        if alterToolbar == nil
        {
            // Should always be the case
            if let tbView = tb.superview
            {
                // Create a new Toolbar and apply correct constraints
                alterToolbar = UIToolbar()
                alterToolbar!.isTranslucent = true
                alterToolbar!.translatesAutoresizingMaskIntoConstraints = false
                tb.isHidden = true
                tbView.addSubview(alterToolbar!)
                if tbView.traitCollection.verticalSizeClass == .compact
                {
                    alterToolbarHeightConstraint = alterToolbar!.heightAnchor.constraint(equalToConstant: 32.0)
                }
                else
                {
                    alterToolbarHeightConstraint = alterToolbar!.heightAnchor.constraint(equalToConstant: 44.0)
                }
                let bottomAnchor: NSLayoutConstraint
                if #available(iOS 11.0, *)
                {
                    bottomAnchor = alterToolbar!.bottomAnchor.constraint(equalTo: tbView.safeAreaLayoutGuide.bottomAnchor)
                }
                else
                {
                    bottomAnchor = alterToolbar!.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor)
                }
                NSLayoutConstraint.activate([
                    alterToolbar!.leadingAnchor.constraint(equalTo: tbView.leadingAnchor),
                    alterToolbar!.trailingAnchor.constraint(equalTo: tbView.trailingAnchor),
                    bottomAnchor,
                    alterToolbarHeightConstraint!
                    ])
                tbView.updateFocusIfNeeded()
                tbView.layoutIfNeeded()
            }
        }
        // Add the original items to the new toolbox
        alterToolbar!.setItems(tb.items, animated: false)
    }
}

func endFixIPhoneXToolbarBug()
{
    if alterToolbar != nil
    {
        alterToolbar!.removeFromSuperview()
        alterToolbar = nil
        alterToolbarHeightConstraint = nil

        if let tb:UIToolbar = self.navigationController?.toolbar
        {
            tb.isHidden = false
        }
    }
}
于 2017-12-07T11:14:54.760 回答