Auto Layout iOS 11 Toolbar UIBarButtonItem with customView
Auto Layout iOS 11 Toolbar UIBarButtonItem with customView
最近在我们的项目中,使用自定义视图的 UIBarButtonItem 出现问题。在 iOS 11 之前,我们通过灵活的间距项进行布局。这不再有效,因此没有显示任何内容。
因为我没有在 SO 上找到真正为我解决问题的答案,所以我对其进行了调查并想出了一个(诚然有点老套)的解决方案,我想与您分享。
也许它可以帮助您或者您有一些反馈。这是混合 objc 和 swift 代码,希望您不要介意。
如 WWDC 视频所示Updating Your App for iOS 11:
"And so now in iOS 11, UI toolbar and UI navigation bar both have
intricate and express support for auto layout."
所以我的第一步是对自定义视图本身使用布局约束:
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:customView];
[barButtonItem.customView.widthAnchor constraintEqualToConstant:375].active = YES;
[barButtonItem.customView.heightAnchor constraintEqualToConstant:44].active = YES;
这导致工具栏显示自定义视图。问题是视图的左侧和右侧存在间隙。你可以看到它。
所以我查看了 View Hierarchy Debugging 工具并意识到,工具栏上有一个 UIToolbarContentView。这个 contentView 有合适的大小(特别是宽度),我开始怀疑。我查看了 contentView 的唯一子视图,它是一个 UIBarButtonStackView。这个 stackView 在宽度方面以某种方式限制了我的 customView。
所以看起来像这样:
contentView |<-fullWidth-------->|
stackView |<-reducedWidth->|
customView |<-reducedWidth->|
让我好奇的是,customView 不是 stackView 的子视图。这可能是自定义视图包含在 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' 函数是序列的扩展。它 'filter.first' 看起来像这样:
extension Sequence {
func find(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> Self.Element? {
return try filter(isIncluded).first
}
}
最近在我们的项目中,使用自定义视图的 UIBarButtonItem 出现问题。在 iOS 11 之前,我们通过灵活的间距项进行布局。这不再有效,因此没有显示任何内容。
因为我没有在 SO 上找到真正为我解决问题的答案,所以我对其进行了调查并想出了一个(诚然有点老套)的解决方案,我想与您分享。
也许它可以帮助您或者您有一些反馈。这是混合 objc 和 swift 代码,希望您不要介意。
如 WWDC 视频所示Updating Your App for iOS 11:
"And so now in iOS 11, UI toolbar and UI navigation bar both have intricate and express support for auto layout."
所以我的第一步是对自定义视图本身使用布局约束:
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:customView];
[barButtonItem.customView.widthAnchor constraintEqualToConstant:375].active = YES;
[barButtonItem.customView.heightAnchor constraintEqualToConstant:44].active = YES;
这导致工具栏显示自定义视图。问题是视图的左侧和右侧存在间隙。你可以看到它。 所以我查看了 View Hierarchy Debugging 工具并意识到,工具栏上有一个 UIToolbarContentView。这个 contentView 有合适的大小(特别是宽度),我开始怀疑。我查看了 contentView 的唯一子视图,它是一个 UIBarButtonStackView。这个 stackView 在宽度方面以某种方式限制了我的 customView。
所以看起来像这样:
contentView |<-fullWidth-------->|
stackView |<-reducedWidth->|
customView |<-reducedWidth->|
让我好奇的是,customView 不是 stackView 的子视图。这可能是自定义视图包含在 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' 函数是序列的扩展。它 'filter.first' 看起来像这样:
extension Sequence {
func find(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> Self.Element? {
return try filter(isIncluded).first
}
}