【发布时间】:2017-12-04 02:02:42
【问题描述】:
我有一个视图控制器,我想在其中隐藏某些子视图、标签等,具体取决于特征集合是什么。例如,我在横向压缩时要隐藏的一侧有标签,纵向压缩时要在顶部和底部隐藏标签。
因为我想隐藏界面的整个部分,听起来 UIStackView 会让这更容易。同样,我尝试在 Interface Builder / Xcode (9.1 9B55) 中而不是在代码中做尽可能多的事情,以尽量保持简单。
设计的主要元素是棋盘,它:
- 始终为 1:1 纵横比
- 在视图中尽可能大
然后我想根据当前的特征集合移动和隐藏它周围的标签和其他项目。
我首先构建了一个水平堆栈视图、对齐方式和分布设置为“填充”。其中有两个项目,boardView(紫色)和 labelView(黄色)。
labelView 中有几个标签,在视图本身中设置了约束以用于这些标签的布局。
boardView 具有纵横比 1:1 (@1000) 的约束:
我为 Stack View 设置了以下约束,将其固定到超级视图(底部除外):
- Safe Area.trailing = Stack View.trailing(@1000)
- Safe Area.leading = Stack View.leading (@1000)
- Safe Area.top = Stack View.top(@1000)
- Safe Area.bottom >= Stack View.bottom(@1000)
我设置了这些约束以确保 boardView 永远不会溢出 superview:
- boardView.width
- boardView.height
然后我将这些约束设置为较低的优先级,这样,如果可能,宽度或高度将扩展以填充超级视图(但不会溢出,因为这些优先级低于上述优先级):
- boardView.width = Safe Area.width (@250)
- boardView.height = 安全区域.height (@250)
这一切似乎都很好。 Xcode 中没有错误,应用程序的行为与我预期的一样,纵向和横向(这些屏幕截图来自 iPhone 8 模拟器):
当我试图隐藏正确的视图时,问题就来了。当我们垂直处于常规模式时,我试图通过隐藏 nameView 来做到这一点。我选择nameView,点击Installed复选框旁边的+,选择hR:
然后取消选中 hR 的已安装框:
这在 Xcode 中看起来很棒。没有错误,IB 似乎在横向和纵向(隐藏了名称标签)中都显示了我所期望的所有视图:
到目前为止,一切都很好。事实上,当我运行它时,它最初看起来和我们预期的一样。如果我们在风景中启动模拟器,它看起来不错:
如果我们以纵向启动模拟器,它看起来不错:
那么问题是什么?好吧,一旦我们从纵向旋转到横向,布局就完全错误了:
同时,我们在控制台中得到一个 LayoutConstraints 错误:
2017-12-03 19:25:55.710381-0600 TestLayoutSIngleView[31439:3231004] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fixes it.
(
"<NSLayoutConstraint:0x60400009b8a0 UIView:0x7fe920603e90.width == UIView:0x7fe920603e90.height (active)>",
"<NSLayoutConstraint:0x60000009ced0 UIStackView:0x7fe92050bd70.leading == UILayoutGuide:0x6000001b5d20'UIViewSafeAreaLayoutGuide'.leading (active)>",
"<NSLayoutConstraint:0x60000009d100 UIStackView:0x7fe92050bd70.top == UILayoutGuide:0x6000001b5d20'UIViewSafeAreaLayoutGuide'.top (active)>",
"<NSLayoutConstraint:0x60000009d150 UILayoutGuide:0x6000001b5d20'UIViewSafeAreaLayoutGuide'.trailing == UIStackView:0x7fe92050bd70.trailing (active)>",
"<NSLayoutConstraint:0x60000009d1a0 UILayoutGuide:0x6000001b5d20'UIViewSafeAreaLayoutGuide'.bottom >= UIStackView:0x7fe92050bd70.bottom (active)>",
"<NSLayoutConstraint:0x60000009e5a0 'UISV-canvas-connection' UIStackView:0x7fe92050bd70.leading == UIView:0x7fe920603e90.leading (active)>",
"<NSLayoutConstraint:0x60000009ce30 'UISV-canvas-connection' UIStackView:0x7fe92050bd70.top == UIView:0x7fe920603e90.top (active)>",
"<NSLayoutConstraint:0x60000009e5f0 'UISV-canvas-connection' V:[UIView:0x7fe920603e90]-(0)-| (active, names: '|':UIStackView:0x7fe92050bd70 )>",
"<NSLayoutConstraint:0x60000009cde0 'UISV-canvas-connection' H:[UIView:0x7fe920603e90]-(0)-| (active, names: '|':UIStackView:0x7fe92050bd70 )>",
"<NSLayoutConstraint:0x60000009e7d0 'UIView-Encapsulated-Layout-Height' UIView:0x7fe92050b8f0.height == 375 (active)>",
"<NSLayoutConstraint:0x60000009e780 'UIView-Encapsulated-Layout-Width' UIView:0x7fe92050b8f0.width == 667 (active)>",
"<NSLayoutConstraint:0x60000009d060 'UIViewSafeAreaLayoutGuide-bottom' V:[UILayoutGuide:0x6000001b5d20'UIViewSafeAreaLayoutGuide']-(0)-| (active, names: '|':UIView:0x7fe92050b8f0 )>",
"<NSLayoutConstraint:0x60000009d010 'UIViewSafeAreaLayoutGuide-left' H:|-(0)-[UILayoutGuide:0x6000001b5d20'UIViewSafeAreaLayoutGuide'](LTR) (active, names: '|':UIView:0x7fe92050b8f0 )>",
"<NSLayoutConstraint:0x60000009d0b0 'UIViewSafeAreaLayoutGuide-right' H:[UILayoutGuide:0x6000001b5d20'UIViewSafeAreaLayoutGuide']-(0)-|(LTR) (active, names: '|':UIView:0x7fe92050b8f0 )>",
"<NSLayoutConstraint:0x60000009cfc0 'UIViewSafeAreaLayoutGuide-top' V:|-(0)-[UILayoutGuide:0x6000001b5d20'UIViewSafeAreaLayoutGuide'] (active, names: '|':UIView:0x7fe92050b8f0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60400009b8a0 UIView:0x7fe920603e90.width == UIView:0x7fe920603e90.height (active)>
我喜欢使用 UIStackView 来简化自动布局和隐藏视图的想法。但是,如果我在 Xcode 中看到的内容在编译和运行时不起作用,那么这似乎会很棘手。
我做错了什么(或者我遇到了 Xcode 错误)?
我创建了一个 GitHub 项目来演示上述内容: https://github.com/johnstewart/TestLayoutSIngleView
编辑......当我写完以上所有内容时,我突然想到,如果我们要在横向模式下隐藏这个视图,布局将无法满足......但我不是将此视图隐藏在横向中,仅纵向。出于某种原因,iOS 在旋转之前对布局进行了更改。
如果我将它的优先级从 1000 设置为 750:
- Safe Area.trailing = Stack View.trailing(@1000)
...我在控制台上没有收到任何自动布局错误。但是,一旦旋转后,横向视图就根本没有正确的位置。
就好像堆栈视图在开始从纵向旋转到横向时立即缩小,并在旋转完成后使堆栈视图处于缩小状态,即使现在有足够的空间:
我真诚地希望我在这里遗漏了一些明显的东西......否则我看不出使用 UIStackView 卸载基于特征集合的视图是多么可行;这似乎是一个简单的示例(确实如此;我还有很多东西要添加!)
编辑:
我看到这种技术的 WWDC 视频来自 WWDC2017,Interface Builder 中的自动布局技术 (https://developer.apple.com/videos/play/wwdc2017/412/)。 29:00 开始演示。
但是,我错了,它是用于此的“已安装”属性。正如公认的答案所示,“隐藏”是这样做的方法。
【问题讨论】:
-
更新 20171205 - 更新到 Xcode 9.2 (9C40b) 并且行为相同。
标签: xcode interface-builder uistackview