ios 带有隐藏 UIViews 的 AutoLayout?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19561269/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
AutoLayout with hidden UIViews?
提问by Ryan Romanchuk
I feel like it's a fairly common paradigm to show/hide UIViews
, most often UILabels
, depending on business logic. My question is, what is the best way using AutoLayout to respond to hidden views as if their frame was 0x0. Here is an example of a dynamic list of 1-3 features.
我觉得根据业务逻辑,显示/隐藏是一种相当常见的范式UIViews
,最常见的是UILabels
。我的问题是,使用 AutoLayout 响应隐藏视图的最佳方法是什么,就好像它们的框架是 0x0 一样。这是一个包含 1-3 个功能的动态列表的示例。
Right now I have a 10px top space from the button to the last label, which obviously won't slide up when the the label is hidden. As of right now I created an outlet to this constraint and modifying the constant depending on how many labels I'm displaying. This is obviously a bit hacky since I'm using negative constant values to push the button up over the hidden frames. It's also bad because it's not being constrained to actual layout elements, just sneaky static calculations based on known heights/padding of other elements, and obviously fighting against what AutoLayout was built for.
现在我从按钮到最后一个标签有一个 10px 的顶部空间,当标签隐藏时显然不会向上滑动。截至目前,我为此约束创建了一个出口,并根据我显示的标签数量修改常量。这显然有点麻烦,因为我使用负常数值将按钮向上推到隐藏框架上。这也很糟糕,因为它不受实际布局元素的限制,只是基于其他元素的已知高度/填充的偷偷摸摸的静态计算,并且显然与 AutoLayout 的构建目的相抗衡。
I could obviously just create new constraints depending on my dynamic labels, but that's a lot of micromanaging and a lot of verbosity for trying to just collapse some whitespace. Are there better approaches? Changing frame size 0,0 and letting AutoLayout do its thing with no manipulation of constraints? Removing views completely?
显然,我可以根据我的动态标签创建新的约束,但这需要大量的微观管理和大量冗长的尝试来折叠一些空白。有更好的方法吗?更改帧大小 0,0 并让 AutoLayout 在不操作约束的情况下完成它的工作?完全删除视图?
Honestly though, just modifying the constant from context of the hidden view requires a single line of code with simple calculation. Recreating new constraints with constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
seems so heavy.
老实说,仅仅从隐藏视图的上下文中修改常量需要一行代码和简单的计算。重新创建新的约束 constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
似乎很重。
Edit Feb 2018: See Ben's answer with UIStackView
s
2018 年 2 月编辑:用UIStackView
s查看 Ben 的回答
采纳答案by Ben Packard
UIStackView
is probably the way to go for iOS 9+. Not only does it handle the hidden view, it will also remove additional spacing and margins if set up correctly.
UIStackView
可能是 iOS 9+ 的方法。它不仅处理隐藏视图,如果设置正确,它还将删除额外的间距和边距。
回答by Max MacLeod
My personal preference for showing/hiding views is to create an IBOutlet with the appropriate width or height constraint.
我个人对显示/隐藏视图的偏好是创建一个具有适当宽度或高度约束的 IBOutlet。
I then update the constant
value to 0
to hide, or whatever the value should be to show.
然后我更新要隐藏的constant
值0
,或者应该显示的任何值。
The big advantage of this technique is that relative constraints will be maintained. For example let's say you have view A and view B with a horizontal gap of x. When view A width constant
is set to 0.f
then view B will move left to fill that space.
这种技术的一大优点是将保持相对约束。例如,假设您有视图 A 和视图 B,其水平间隙为x。当视图 A 宽度constant
设置为 时,0.f
视图 B 将向左移动以填充该空间。
There's no need to add or remove constraints, which is a heavyweight operation. Simply updating the constraint's constant
will do the trick.
无需添加或删除约束,这是一项重量级操作。只需更新约束即可解决问题constant
。
回答by SimplGy
The solution of using a constant 0
when hidden and another constant if you show it again is functional, but it is unsatisfying if your content has a flexible size. You'd need to measure your flexible content and set a constant back. This feels wrong, and has issues if content changes size because of server or UI events.
0
隐藏时使用常量而再次显示时使用另一个常量的解决方案是实用的,但如果您的内容具有灵活的大小,则令人不满意。您需要衡量您的灵活内容并设置一个常数。这感觉不对,如果内容因服务器或 UI 事件而改变大小,则会出现问题。
I have a better solution.
我有一个更好的解决方案。
The idea is to set the a 0 height rule to have high priority when we hide the element so that it takes up no autolayout space.
这个想法是在我们隐藏元素时设置 0 高度规则具有高优先级,以便它不占用自动布局空间。
Here's how you do that:
这是你如何做到的:
1. set up a width (or height) of 0 in interface builder with a low priority.
1.在低优先级的interface builder中设置宽度(或高度)为0。
Interface Builder won't yell about conflicts because the priority is low. Test the height behavior by setting the priority to 999 temporarily (1000 is forbidden to mutate programmatically, so we won't use it). Interface builder will probably now yell about conflicting constraints. You can fix these by setting priorities on related objects to 900 or so.
Interface Builder 不会因为优先级低而大喊大叫。通过临时设置优先级为 999 来测试高度行为(1000 禁止以编程方式变异,因此我们不会使用它)。界面构建器现在可能会大喊大叫冲突的约束。您可以通过将相关对象的优先级设置为 900 左右来解决这些问题。
2. Add an outlet so you can modify the priority of the width constraint in code:
2.添加一个outlet,可以在代码中修改宽度约束的优先级:
3. Adjust the priority when you hide your element:
3. 隐藏元素时调整优先级:
cell.alertTimingView.hidden = place.closingSoon != true
cell.alertTimingWidth.priority = place.closingSoon == true ? 250 : 999
回答by Mitul Marsoniya
In this case, I map the height of the Author label to an appropriate IBOutlet:
在这种情况下,我将 Author 标签的高度映射到适当的 IBOutlet:
@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;
and when I set the height of the constraint to 0.0f, we preserve the "padding", because the Play button's height allows for it.
当我将约束的高度设置为 0.0f 时,我们保留了“填充”,因为播放按钮的高度允许它。
cell.authorLabelHeight.constant = 0; //Hide
cell.authorLabelHeight.constant = 44; //Show
回答by SeanR
There's a lot of solutions here but my usual approach is different again :)
这里有很多解决方案,但我通常的方法又有所不同:)
Set up two sets of constraints similar to Jorge Arimany's and TMin's answer:
设置两组类似于 Jorge Arimany 和 TMin 的回答的约束:
All three marked constraints have a the same value for the Constant. The constraints marked A1 and A2 have their Priority set to 500, while the constraint marked B has it's Priority set to 250 (or UILayoutProperty.defaultLow
in code).
所有三个标记的约束都具有相同的常量值。标记为 A1 和 A2 的约束将其优先级设置为 500,而标记为 B 的约束将其优先级设置为 250(或UILayoutProperty.defaultLow
在代码中)。
Hook up constraint B to an IBOutlet. Then, when you hide the element, you just need to set the constraint priority to high (750):
将约束 B 连接到 IBOutlet。然后,当你隐藏元素时,你只需要将约束优先级设置为高(750):
constraintB.priority = .defaultHigh
constraintB.priority = .defaultHigh
But when the element is visible, set the priority back to low (250):
但是当元素可见时,将优先级设置回低(250):
constraintB.priority = .defaultLow
constraintB.priority = .defaultLow
The (admittedly minor) advantage to this approach over just changing isActive
for constraint B is that you still have a working constraint if the transient element gets removed from the view by other means.
与仅更改isActive
约束 B相比,这种方法的(不可否认的)优势在于,如果瞬态元素通过其他方式从视图中删除,您仍然有一个工作约束。
回答by Daniel
Subclass the view and override func intrinsicContentSize() -> CGSize
. Just return CGSizeZero
if the view is hidden.
子类化视图并覆盖func intrinsicContentSize() -> CGSize
。CGSizeZero
如果视图被隐藏,只需返回。
回答by Albert Bori
I'm surprised that there is not a more elegant approach provided by UIKit
for this desired behavior. It seems like a very common thing to want to be able to do.
我很惊讶没有UIKit
为这种所需的行为提供更优雅的方法。想要能够做到这一点似乎是一件很常见的事情。
Since connecting constraints to IBOutlets
and setting their constants to 0
felt yucky (and caused NSLayoutConstraint
warnings when your view had subviews), I decided to create an extension that gives a simple, stateful approach to hiding/showing a UIView
that has Auto Layout constraints
由于连接约束IBOutlets
并将其常量设置为0
令人讨厌(并NSLayoutConstraint
在您的视图有子视图时引起警告),我决定创建一个扩展,它提供了一种简单的、有状态的方法来隐藏/显示UIView
具有自动布局约束的
It merely hides the view and removes exterior constraints. When you show the view again, it adds the constraints back. The only caveat is that you'll need to specify flexible failover constraints to surrounding views.
它只是隐藏视图并移除外部约束。当您再次显示视图时,它会重新添加约束。唯一需要注意的是,您需要为周围的视图指定灵活的故障转移约束。
EditThis answer is targeted at iOS 8.4 and below. In iOS 9, just use the UIStackView
approach.
编辑此答案针对 iOS 8.4 及更低版本。在 iOS 9 中,只需使用该UIStackView
方法。
回答by Matt Koala
I just found out that to get a UILabel to not take up space, you have to hide it AND set its text to an empty string. (iOS 9)
我刚刚发现要让 UILabel 不占用空间,您必须隐藏它并将其文本设置为空字符串。(iOS 9)
Knowing this fact/bug could help some people simplify their layouts, possibly even that of the original question, so I figured I'd post it.
知道这个事实/错误可以帮助一些人简化他们的布局,甚至可能是原始问题的布局,所以我想我会发布它。
回答by Jorge Arimany
The best practice is, once everything has the correct layout constraints, add a height or with constraint, depending how you want the surrounding views to move and connect the constraint into an IBOutlet
property.
最佳实践是,一旦所有内容都具有正确的布局约束,添加高度或带约束,具体取决于您希望周围视图如何移动并将约束连接到IBOutlet
属性中。
Make sure that your properties are strong
确保您的属性是 strong
in code yo just have to set the constant to 0 and activateit, tho hide the content, or deactivateit to show the content.
This is better than messing up with the constant value an saving-restoring it.
Do not forget to call layoutIfNeeded
afterwards.
在代码中,您只需将常量设置为 0 并激活它,隐藏内容,或停用它以显示内容。这比弄乱常量值并保存恢复它要好。之后不要忘记打电话layoutIfNeeded
。
If the content to be hidden is grouped, the best practice is to put all into a view and add the constraints to that view
如果要隐藏的内容是分组的,最好的做法是将所有内容放入一个视图中,并在该视图中添加约束
@property (strong, nonatomic) IBOutlet UIView *myContainer;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *myContainerHeight; //should be strong!!
-(void) showContainer
{
self.myContainerHeight.active = NO;
self.myContainer.hidden = NO;
[self.view layoutIfNeeded];
}
-(void) hideContainer
{
self.myContainerHeight.active = YES;
self.myContainerHeight.constant = 0.0f;
self.myContainer.hidden = YES;
[self.view layoutIfNeeded];
}
Once you have your setup you can test it in IntefaceBuilder by setting your constraint to 0 and then back to the original value. Don't forget to check other constraints priorities so when hidden there is no conflict at all. other way to test it is to put it to 0 and set the priority to 0, but, you should not forget to restore it to the highest priority again.
完成设置后,您可以在 IntefaceBuilder 中测试它,方法是将约束设置为 0,然后返回到原始值。不要忘记检查其他约束优先级,以便隐藏时根本没有冲突。另一种测试方法是将其设置为0并将优先级设置为0,但是,您不应忘记再次将其恢复为最高优先级。
回答by Damien Romito
I build category to update constraints easily:
我构建类别以轻松更新约束:
[myView1 hideByHeight:YES];
Answer here: Hide autolayout UIView : How to get existing NSLayoutConstraint to update this one
在这里回答: 隐藏自动布局 UIView : How to get existing NSLayoutConstraint to update this one