UINavigationBar使用弹性空间均匀分布条形按钮项

UINavigationBar using flexible spaces to distribute bar button items evenly

在基于 UINavigationController 的应用程序中,导航控制器的工具栏已打开,因此顶部有 UINavigationBar,底部有 UIToolbar。

对于根视图控制器,显然没有后退按钮,也没有标题(也没有标题视图)。我设置了 title = nil.

同一个根视图控制器在导航栏 rightBarButtonItems 中有很多栏按钮项。由于除了这些项目之外导航栏中没有其他内容,我希望它们在导航栏中居中并均匀 spaced。这样,它们看起来不那么拥挤,不太可能触错按钮,总体上看起来更漂亮,并且也与底部工具栏的外观相匹配。

在底部工具栏中,这可以通过在每个项目之间使用灵活的 space 轻松实现(在我的例子中 before/after first/last 项目)。

对于顶部导航栏,灵活的 spaces(和固定的 spaces)似乎被忽略了。

当栏上没有其他内容时,如何让导航栏中的栏按钮项目分布在整个栏中?

或者是隐藏栏的唯一选项,实际上在顶部放置一个独立的工具栏? (不得不求助于此将是一种耻辱,因为导航栏已经存在并且几乎可以完成工作并且是自动管理的。)

以下是我如何完成在导航栏中使用灵活的 spaces 来均匀地 space 出所有栏按钮项目的近似方法。

注意:就目前而言,它仅适用于带有图像的栏按钮项目。纯文本项目不起作用,因为它使用图像大小来确定项目的宽度。更新它以使用文本或 text/image 项应该不会太难。

它的工作原理是将所有条形按钮项目中所有图像的总宽度相加,然后从条形的大小中减去它以获得剩余的 space 量。然后将其除以项目数(加一,因为我想要在所有项目前后添加 space)并调整屏幕比例。

将此设置为新固定 space 项目的宽度。

重新添加这些项目,在每个项目之前、之间和之后添加新的 space。

    func updateNavbarItemSpacing(forBarWidth width: CGFloat) {
        guard let oldItems = navigationItem.leftBarButtonItems else { return }
        var nonSpaceItems: [UIBarButtonItem] = []
        var usedWidth: CGFloat = 0.0
        var itemCount:CGFloat = 0.0
        
        for item in oldItems {
            if let itemWidth = item.image?.size.width {
                usedWidth += itemWidth
                itemCount += 1
                nonSpaceItems.append(item)
            }
        }
        
        let spaceWidth = ( ( width - usedWidth) / itemCount + 1 ) / UIScreen.main.scale
        let fixedSpace = UIBarButtonItem.fixedSpace(spaceWidth)
        
        var newItems: Array<UIBarButtonItem> = [fixedSpace]
        
        for item in nonSpaceItems {
            newItems.append(item)
            newItems.append(fixedSpace)
        }
        
        navigationItem.leftBarButtonItems = newItems
    }

每当项目更新时,以及导航栏大小发生变化时,都应调用此函数。例如,不要忘记将它包含在视图控制器中:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        
        updateNavbarItemSpacing(forBarWidth: size.width)
    }

目前在测试中运行良好。完美的肖像模式。但是在 lasdcape 方向上,我最后得到了一些额外的 space。尚未弄清楚这一点。

我现在找到了一种更简单的方法来实现这一目标。

此版本简单地将可用 space 分配给项目数。

减去 2 点似乎效果更好 - 如果没有这个,结果在拥挤的工具栏中就不那么令人满意了。我想这允许每个项目之间留出很小的余量?

请注意,如果某些项目的 text/image 比其他项目宽,则每个项目之间的间距将不均匀。所以这种方法最适合宽度相近的物品。

func updateNavbarItemSpacing(forBarWidth barWidth: CGFloat) {
        guard let items = navigationItem.leftBarButtonItems  else { return }
        if items.count == 0 { return }
        let itemWidth = barWidth / CGFloat(items.count) - 2
        for item in items {
            item.width = itemWidth
        }
        
        navigationItem.leftBarButtonItems = items
    }

设置navigationItem的左toobar项后调用上面的方法。

此外,为了在旋转设备时获得正确的行为,请在视图控制器中调用它:

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        
        updateNavbarItemSpacing(forBarWidth: size.width)
    }