带有customView的自动布局iOS 11工具栏UIBarButtonItem

Posted

技术标签:

【中文标题】带有customView的自动布局iOS 11工具栏UIBarButtonItem【英文标题】:Auto Layout iOS 11 Toolbar UIBarButtonItem with customView 【发布时间】:2017-10-05 08:38:07 【问题描述】:

最近在我们的项目中,使用 customView 的 UIBarButtonItem 出现问题。在 ios 11 之前,我们通过灵活间距项进行布局。这不再起作用,因此没有显示任何内容。

因为我在 SO 上没有找到真正为我解决问题的答案,所以我对其进行了调查,并提出了一个(诚然有点老套的)解决方案,我想与您分享。

也许它可以帮助你或者你有一些反馈。这是混合了 objc 和 swift 代码,希望你不要介意。

【问题讨论】:

【参考方案1】:

如WWDC视频Updating Your App for iOS 11所示:

“所以现在在 iOS 11 中,UI 工具栏和 UI 导航栏都有 对自动布局的复杂而明确的支持。”

所以我的第一步是在自定义视图本身上使用布局约束:

    UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:customView];
    [barButtonItem.customView.widthAnchor constraintEqualToConstant:375].active = YES;
    [barButtonItem.customView.heightAnchor constraintEqualToConstant:44].active = YES;

这导致工具栏显示 customView。问题是视图的左右,有一个差距。你可以看到它。 于是我查看了View Hierarchy Debugging工具,发现工具栏上有一个UIToolbarContentView。这个 contentView 有合适的尺寸(尤其是宽度),我开始怀疑。我查看了 contentView 的唯一子视图,它是 UIBarButtonStackView。这个 stackView 在某种程度上限制了我的 customView 的宽度。

所以它看起来像这样:

contentView |<-fullWidth-------->|
stackView     |<-reducedWidth->|
customView    |<-reducedWidth->|

让我感到好奇的是,customView 不是 stackView 的子视图。这可能是 UIBarButtonItem 中包含 customView 的结果。 customView 上的任何(附加)约束保持不变(或由于视图不在同一层次结构中而崩溃)。

在了解了所有这些之后,我在 UIToolbar 中添加了一个扩展:

extension UIToolbar 
    private var contentView: UIView? 
        return subviews.find  (view) -> Bool in
            let viewDescription = String(describing: type(of: view))
            return viewDescription.contains("ContentView")
        
    

    private var stackView: UIView? 
        return contentView?.subviews.find  (view) -> Bool in
            let viewDescription = String(describing: type(of: view))
            return viewDescription.contains("ButtonBarStackView")
        
    

   func fitContentViewToToolbar() 
        guard let stackView = stackView, let contentView = contentView else  return 
        stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
        stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
        stackView.widthAnchor.constraint(equalTo: contentView.widthAnchor).isActive = true

    

所以它的作用是: 它通过将名称与“ContentView”进行比较来从子视图中获取 contentView,并通过对 contentView 执行相同操作来获取 stackView。 可能return subviews.first 也会这样做,但我想确定一下。

然后设置布局约束,瞧:它以全宽工作。

我希望有人会觉得这很有用。如果有 cmets:我非常愿意接受对此的反馈。也许我错过了什么,而这一切都没有必要。

编辑:“查找”功能是对序列的扩展。它执行“filter.first”,看起来像这样:

extension Sequence 
    func find(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> Self.Element? 
        return try filter(isIncluded).first

【讨论】:

间隙可能由一些默认的 layoutMargins 定义,通常每边有 8 个点。出于某种原因,这里他们是 16 分 H:|-(16)-[_UIButtonBarStackView 上述解决方案与已经为stackView定义的一些约束冲突..."&lt;NSLayoutConstraint:0x60000028acd0 _UIButtonBarStackView:0x7ffbced16f10.trailing == _UIToolbarContentView:0x7ffbced16780.trailing - 16 (active)&gt;"! 感谢您的提醒。我没有注意到。也许可以事先删除这些。尽管这感觉越来越像是在摆弄 UIKit 内部...... 提供的解决方案使用私有 API。我仍在尝试为此寻找解决方案。如果我找到它会发布。 @user1447414 您是否设法在不使用私有 API 的情况下找到了解决方案

以上是关于带有customView的自动布局iOS 11工具栏UIBarButtonItem的主要内容,如果未能解决你的问题,请参考以下文章

带有 customView 的 UIBarButtonItem 在 iOS4.1 中消失了

iOS:带有 1 个标签和 3 个按钮的简单自动布局

initWithCoder 在 ios 中显示错误的帧

iOS - 自定义视图:在 layoutSubviews() 中更新后忽略了固有内容大小

iOS mapview 自动布局问题

iOS:多行 uilabel 仅显示带有自动布局的一行