自动布局iOS 11工具栏UIBarButtonItem与自定义视图

3

我们项目最近遇到了一个使用自定义视图的UIBarButtonItem问题。在iOS 11之前,我们通过可伸缩空间项进行布局。但是在iOS 11上不再起作用,因此什么都没有显示出来。

由于我在这里没有找到真正解决该问题的答案,所以我进行了研究,并想与您分享(尽管有点取巧的)解决方案。

也许它可以帮助您或者您有一些反馈。这是混合了Objective-C和Swift代码,希望您不介意。

1个回答

11

根据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;

这导致工具栏显示了自定义视图。问题是,在该视图的左右两侧有一段空隙,而且可以看到它。 于是我使用查看视图层次结构调试工具,并注意到在工具栏上有一个UIToolbarContentView。该contentView的大小(特别是宽度)是正确的,这让我开始思考。我查看了contentView仅有的子视图,并发现它是一个UIBarButtonStackView。这个stackView在宽度方面以某种方式限制了我的自定义视图。

所以就像这样:

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

    }
}
所以它的作用是这样的:通过将名称与“ContentView”进行比较,从子视图中获取contentView,并通过在contentView上执行相同操作来获取stackView。 可能一个return subviews.first也可以达到相同的效果,但我想要确保。 然后设置布局约束,完成:它可以在完整的宽度下工作。 希望有人会发现这个有用。如果有评论:我非常乐意听取这个的反馈。也许我错过了什么,甚至这一切都不必要。 编辑:'find'函数是Sequence的扩展。 它执行'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 - grethi
谢谢提醒,我没有注意到。也许可以事先删除那些。尽管这感觉越来越像在操作UIKit内部... - lucasl
提供的解决方案使用了私有API。我仍在努力寻找解决方案。如果我找到了,我会发布的。 - user1447414
@user1447414,你能否在不使用私有API的情况下找到解决方案? - JAHelia
@JAHelia 是的,我有。对于iOS 11,您需要完全使用Autolayout,而无需覆盖私有API。我不知道您的情况是什么。也许在SO上发布一个清晰的问题? - user1447414
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接