自动布局:如何压缩复杂的弹出框
Autolayout: How to compress a complex popover
如果您想尝试源代码(非常欢迎您这样做),看看我的Bitbucket repository。
我有一个显示设置列表的弹出对话框。这些设置列在多个 UITableView 中。 UITableViews 不应该是可滚动的,因为整体设置视图已经是。此外,弹出对话框应根据需要垂直占据尽可能多的屏幕,但应水平压缩。
于是,我构思了如下结构:
UIView => MySettingsViewController
- UIScrollView
- UIView (Content View)
- Container View1
- UITableView (embedded) => MyTableViewController
- Container View2
- UITableView (embedded)
该结构是通过 Interface Builder 组装的,Autolayout 用于调整大小。
我有滚动视图、内容视图(我从一个开始)和容器视图到它们各自的超级视图(或布局指南)。我通过以下方式限制了内容视图的大小:
contentView.width == (topmost) UIView.width
contentView.height == 200 // removed at build time
此外,我将 table 视图的大小设置为它的内容大小,否则弹出框看起来是空的:
class MyTableViewController: UITableViewController {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// this is Cartography syntax - the intention should be clear
layout(view, replace: ConstraintGroup()) { [unowned self] view in
view.width == self.tableView.contentSize.width
view.height == self.tableView.contentSize.height
}
view.setNeedsLayout()
}
}
设置弹出框内容满了,但是大小不太对:
为了解决这个问题,我尝试了以下无效的方法:
class MySettingsViewController: UIViewController {
override var preferredContentSize: CGSize {
get {
let compressedSize = view.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
// this is always (0, 0) because the subviews are not resized, yet
return compressedSize
}
set {
super.preferredContentSize = newValue
}
}
}
结论:压缩不起作用。
所以我自己解决了这个问题,正如您在查看 Bitbucket 存储库时看到的那样。
布局现已在 MyTableViewController
和 MySettingsViewController
中得到修复。前一个现在看起来像这样:
class MyTableViewController: UITableViewController {
var heightConstraint: NSLayoutConstraint?
var tableViewEdgesConstraints: [NSLayoutConstraint]?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if let container = tableView.superview where tableViewEdgesConstraints == nil {
layout(tableView, container, replace: ConstraintGroup()) { [unowned self] tableView, container in
self.tableViewEdgesConstraints = tableView.edges == inset(container.edges, 0)
}
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let heightConstraint = heightConstraint {
if Int(heightConstraint.constant) != Int(tableView.contentSize.height) {
heightConstraint.constant = self.tableView.contentSize.height
}
} else {
layout(view, replace: ConstraintGroup()) { [unowned self] view in
if (self.tableView.contentSize.height > 0) {
self.heightConstraint = view.height == self.tableView.contentSize.height
}
}
}
}
}
所以基本上,我将 table 的高度限制为其内容的高度,并在内容的高度发生变化时更改限制。一旦 table 布局完成,就会完成此操作。此外,嵌套的 table 视图通过其边缘固定到容器视图的边缘。我认为这是强制性的,因为我无法找到如何在 Interface Builder 中限制不同场景的两个视图。
在 MySettingsViewController
中,滚动视图的大小一旦已知就会设置为内容视图框架(可通过插座访问)的大小。此外,为了使弹出窗口压缩,设置控制器的 preferredContentSize 会相应地进行调整,当高度发生变化时(如果您省略条件,您可能会陷入布局无限循环。此外,我做了 3 件事以使其成为可能环绕 MySettingsViewController 的导航控制器:
- 弹出窗口的宽度设置为固定值(否则有时会扩展到全宽)。
- presentedViewController的preferredContentSize需要设置相等
- 我不得不将 scrollView 的 insets 设置为 0 以避免丑陋的垂直偏移 - 这个解决方案不是最佳的,因为它稍微破坏了滚动视图的体验。但它有效。
代码如下:
class MySettingsViewController: UIViewController {
@IBOutlet weak var contentView: UIView!
@IBOutlet weak var scrollView: UIScrollView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = contentView.frame.size
if (preferredContentSize.height != scrollView.contentSize.height) {
let newSize = CGSize(width: 400, height: scrollView.contentSize.height)
preferredContentSize = newSize
presentingViewController?.presentedViewController?.preferredContentSize = newSize
scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
}
}
这是结果:
如果您想尝试源代码(非常欢迎您这样做),看看我的Bitbucket repository。
我有一个显示设置列表的弹出对话框。这些设置列在多个 UITableView 中。 UITableViews 不应该是可滚动的,因为整体设置视图已经是。此外,弹出对话框应根据需要垂直占据尽可能多的屏幕,但应水平压缩。
于是,我构思了如下结构:
UIView => MySettingsViewController
- UIScrollView
- UIView (Content View)
- Container View1
- UITableView (embedded) => MyTableViewController
- Container View2
- UITableView (embedded)
该结构是通过 Interface Builder 组装的,Autolayout 用于调整大小。
我有滚动视图、内容视图(我从一个开始)和容器视图到它们各自的超级视图(或布局指南)。我通过以下方式限制了内容视图的大小:
contentView.width == (topmost) UIView.width
contentView.height == 200 // removed at build time
此外,我将 table 视图的大小设置为它的内容大小,否则弹出框看起来是空的:
class MyTableViewController: UITableViewController {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// this is Cartography syntax - the intention should be clear
layout(view, replace: ConstraintGroup()) { [unowned self] view in
view.width == self.tableView.contentSize.width
view.height == self.tableView.contentSize.height
}
view.setNeedsLayout()
}
}
设置弹出框内容满了,但是大小不太对:
为了解决这个问题,我尝试了以下无效的方法:
class MySettingsViewController: UIViewController {
override var preferredContentSize: CGSize {
get {
let compressedSize = view.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
// this is always (0, 0) because the subviews are not resized, yet
return compressedSize
}
set {
super.preferredContentSize = newValue
}
}
}
结论:压缩不起作用。
所以我自己解决了这个问题,正如您在查看 Bitbucket 存储库时看到的那样。
布局现已在 MyTableViewController
和 MySettingsViewController
中得到修复。前一个现在看起来像这样:
class MyTableViewController: UITableViewController {
var heightConstraint: NSLayoutConstraint?
var tableViewEdgesConstraints: [NSLayoutConstraint]?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if let container = tableView.superview where tableViewEdgesConstraints == nil {
layout(tableView, container, replace: ConstraintGroup()) { [unowned self] tableView, container in
self.tableViewEdgesConstraints = tableView.edges == inset(container.edges, 0)
}
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let heightConstraint = heightConstraint {
if Int(heightConstraint.constant) != Int(tableView.contentSize.height) {
heightConstraint.constant = self.tableView.contentSize.height
}
} else {
layout(view, replace: ConstraintGroup()) { [unowned self] view in
if (self.tableView.contentSize.height > 0) {
self.heightConstraint = view.height == self.tableView.contentSize.height
}
}
}
}
}
所以基本上,我将 table 的高度限制为其内容的高度,并在内容的高度发生变化时更改限制。一旦 table 布局完成,就会完成此操作。此外,嵌套的 table 视图通过其边缘固定到容器视图的边缘。我认为这是强制性的,因为我无法找到如何在 Interface Builder 中限制不同场景的两个视图。
在 MySettingsViewController
中,滚动视图的大小一旦已知就会设置为内容视图框架(可通过插座访问)的大小。此外,为了使弹出窗口压缩,设置控制器的 preferredContentSize 会相应地进行调整,当高度发生变化时(如果您省略条件,您可能会陷入布局无限循环。此外,我做了 3 件事以使其成为可能环绕 MySettingsViewController 的导航控制器:
- 弹出窗口的宽度设置为固定值(否则有时会扩展到全宽)。
- presentedViewController的preferredContentSize需要设置相等
- 我不得不将 scrollView 的 insets 设置为 0 以避免丑陋的垂直偏移 - 这个解决方案不是最佳的,因为它稍微破坏了滚动视图的体验。但它有效。
代码如下:
class MySettingsViewController: UIViewController {
@IBOutlet weak var contentView: UIView!
@IBOutlet weak var scrollView: UIScrollView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = contentView.frame.size
if (preferredContentSize.height != scrollView.contentSize.height) {
let newSize = CGSize(width: 400, height: scrollView.contentSize.height)
preferredContentSize = newSize
presentingViewController?.presentedViewController?.preferredContentSize = newSize
scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
}
}
这是结果: