使用customView自动布局iOS 11工具栏UIBarButtonItem

时间:2017-10-05 08:38:08

标签: ios autolayout uibarbuttonitem uitoolbar ios11

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

因为我在SO上找不到真正为我解决问题的答案,所以我调查了一下,并提出了一个我想与你分享的(无可置疑的hacky)解决方案。

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

1 个答案:

答案 0 :(得分:10)

如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的子视图。这可能是由于CustomView包含在UIBarButtonItem中的结果。 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

    }
}

它的作用是这样的: 它通过将名称与&#34; ContentView&#34;进行比较,从子视图中获取contentView。并通过在contentView上执行相同操作来获取stackView。 可能return subviews.first会做同样的事,但我想确定。

然后设置布局约束并且瞧:它在全宽度下工作。

我希望有人可能觉得这很有用。如果有评论:我对这一点的反馈非常开放。也许我错过了一些东西,所有这一切都没有必要。

编辑:&#39;发现&#39; function是Sequence的扩展。它确实过滤了第一个&#39;看起来像这样:

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

}