ios 隐藏视图时如何使用自动布局移动其他视图?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18065938/
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
How to use auto-layout to move other views when a view is hidden?
提问by Guferos
I have designed my custom Cell in IB, subclassed it and connected my outlets to my custom class. I have three subviews in cell content which are: UIView (cdView) and two labels (titleLabel and emailLabel). Depending on data available for each row, sometimes I want to have UIView and two labels displayed in my cell and sometimes only two labels. What I am trying to do is to set constraints that way if I set UIView property to hidden or I will remove it from superview the two labels will move to the left. I tried to set UIView leading constraint to Superview (Cell content) for 10px and UILabels leading Constraints for 10 px to the next view (UIView). Later in my code
我在 IB 中设计了我的自定义 Cell,将其子类化并将我的插座连接到我的自定义类。我在单元格内容中有三个子视图:UIView (cdView) 和两个标签(titleLabel 和 emailLabel)。根据每行可用的数据,有时我希望在我的单元格中显示 UIView 和两个标签,有时只显示两个标签。如果我将 UIView 属性设置为 hidden 或者我将从超级视图中删除它,我想要做的是以这种方式设置约束,两个标签将向左移动。我尝试将 UIView 引导约束设置为 Superview(单元格内容)为 10px 和 UILabels 引导约束为 10 px 到下一个视图(UIView)。稍后在我的代码中
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(IndexPath *)indexPath {
...
Record *record = [self.records objectAtIndex:indexPath.row];
if ([record.imageURL is equalToString:@""]) {
cell.cdView.hidden = YES;
}
I am hiding my cell.cdView and I would like the labels to move to the left however they are staying in the same position in Cell. I tried to remove cell.cdView from superview but it didn't work either. I have attached image to clarify what I am about.
我隐藏了我的 cell.cdView 并且我希望标签向左移动,但是它们在 Cell 中保持在相同的位置。我试图从超级视图中删除 cell.cdView 但它也不起作用。我附上了图片来澄清我的意思。
I know how to do this programatically and I am not looking for that solution. What I want is to set constraints in IB and I expect that my subviews will move dynamically if other views are removed or hidden. Is it possible to do this in IB with auto-layout?
我知道如何以编程方式执行此操作,并且我不是在寻找该解决方案。我想要的是在 IB 中设置约束,如果其他视图被删除或隐藏,我希望我的子视图会动态移动。是否可以通过自动布局在 IB 中执行此操作?
.....
回答by Tim
It is possible, but you'll have to do a little extra work. There are a couple conceptual things to get out of the way first:
这是可能的,但您必须做一些额外的工作。有几个概念性的事情要先让开:
- Hidden views, even though they don't draw, still participatein Auto Layout and usually retain their frames, leaving other related views in their places.
- When removing a view from its superview, all related constraintsare also removed from that view hierarchy.
- 隐藏的视图,即使它们不绘制,仍然参与自动布局并通常保留它们的框架,将其他相关的视图留在它们的位置。
- 当从其父视图中删除一个视图时,所有相关的约束也会从该视图层次结构中删除。
In your case, this likely means:
在您的情况下,这可能意味着:
- If you set your left view to be hidden, the labels stay in place, since that left view is still taking up space (even though it's not visible).
- If you remove your left view, your labels will probably be left ambiguously constrained, since you no longer have constraints for your labels' left edges.
- 如果您将左视图设置为隐藏,标签将保持原位,因为该左视图仍在占用空间(即使它不可见)。
- 如果您删除左视图,您的标签可能会受到不明确的约束,因为您不再对标签的左边缘有约束。
What you need to do is judiciously over-constrainyour labels. Leave your existing constraints (10pts space to the other view) alone, but add another constraint: make your labels' left edges 10pts away from their superview's left edge with a non-required priority (the default high priority will probably work well).
您需要做的是明智地过度限制您的标签。保留现有的约束(其他视图的 10pts 空间),但添加另一个约束:使标签的左边缘距其超级视图的左边缘 10pts 并具有非必需的优先级(默认的高优先级可能会很好地工作)。
Then, when you want them to move left, remove the left view altogether. The mandatory 10pt constraint to the left view will disappear along with the view it relates to, and you'll be left with just a high-priority constraint that the labels be 10pts away from their superview. On the next layout pass, this should cause them to expand left until they fill the width of the superview but for your spacing around the edges.
然后,当您希望它们向左移动时,完全删除左视图。左视图的强制性 10pt 约束将与它相关的视图一起消失,您将只剩下一个高优先级约束,即标签距其超视图 10pts。在下一次布局过程中,这应该会导致它们向左扩展,直到它们填满超级视图的宽度,但要保留边缘周围的间距。
One important caveat: if you ever want your left view back in the picture, not only do you have to add it back into the view hierarchy, but you also have to reestablish all its constraintsat the same time. This means you need a way to put your 10pt spacing constraint between the view and its labels back whenever that view is shown again.
一个重要的警告:如果你想让你的左视图重新出现在图片中,你不仅必须将它重新添加到视图层次结构中,而且还必须同时重新建立它的所有约束。这意味着您需要一种方法来在再次显示该视图时将视图与其标签之间的 10pt 间距约束放回原处。
回答by Max MacLeod
Adding or removing constraints during runtime is a heavyweight operation that can affect performance. However, there is a simpler alternative.
在运行时添加或删除约束是可能影响性能的重量级操作。但是,还有一个更简单的选择。
For the view you wish to hide, set up a width constraint. Constrain the other views with a leading horizontal gap to that view.
对于要隐藏的视图,设置宽度约束。使用领先的水平间隙将其他视图约束到该视图。
To hide, update the .constant
of the width constraint to 0.f. The other views will automatically move left to assume position.
要隐藏,.constant
请将宽度约束的更新为 0.f。其他视图将自动向左移动以占据位置。
See my other answer here for more details:
有关更多详细信息,请参阅我的另一个答案:
回答by Silmaril
For those who support iOS 8+only, there is a new boolean property active. It will help to enable only needed constraints dynamically
对于仅支持iOS 8+ 的用户,有一个新的布尔属性active。它将有助于动态启用仅需要的约束
P.S. Constraint outlet must be strong, not weak
Example:
PS 约束出口必须是强的,而不是弱的
示例:
@IBOutlet weak var optionalView: UIView!
@IBOutlet var viewIsVisibleConstraint: NSLayoutConstraint!
@IBOutlet var viewIsHiddenConstraint: NSLayoutConstraint!
func showView() {
optionalView.isHidden = false
viewIsVisibleConstraint.isActive = true
viewIsHiddenConstraint.isActive = false
}
func hideView() {
optionalView.isHidden = true
viewIsVisibleConstraint.isActive = false
viewIsHiddenConstraint.isActive = true
}
Also to fix an error in storyboard you'll need to uncheck Installed
checkbox for one of these constraints.
UIStackView (iOS 9+)
One more option is to wrap your views in UIStackView
. Once view is hidden UIStackView
will update layout automatically
同样要修复情节提要中的错误,您需要取消Installed
选中这些约束之一的复选框。
UIStackView (iOS 9+)另
一种选择是将您的视图包装在UIStackView
. 隐藏视图UIStackView
后将自动更新布局
回答by 5bars
UIStackView
repositions its views automatically when the hidden
property is changed on any of its subviews (iOS 9+).
UIStackView
当hidden
属性在其任何子视图(iOS 9+)上更改时自动重新定位其视图。
UIView.animateWithDuration(1.0) { () -> Void in
self.mySubview.hidden = !self.mySubview.hidden
}
Jump to 11:48in this WWDC video for a demo:
跳转到此 WWDC 视频中的11:48进行演示:
回答by Robert Atkins
My project uses a custom @IBDesignable
subclass of UILabel
(to ensure consistency in colour, font, insets etc.) and I have implemented something like the following:
我的项目使用了一个自定义@IBDesignable
子类UILabel
(以确保颜色、字体、插图等的一致性),并且我已经实现了如下内容:
override func intrinsicContentSize() -> CGSize {
if hidden {
return CGSizeZero
} else {
return super.intrinsicContentSize()
}
}
This allows the label subclass to take part in Auto Layout, but take no space when hidden.
这允许标签子类参与自动布局,但隐藏时不占用空间。
回答by jterry
For the Googlers:building on Max's answer, to solve the padding issue that many have noticed I simply increased the height of the label and used that height as the separator instead of actual padding. This idea could be expanded for any scenario with containing views.
对于 Google 员工:以 Max 的回答为基础,为了解决许多人注意到的填充问题,我只是增加了标签的高度并将该高度用作分隔符而不是实际填充。这个想法可以扩展到任何包含视图的场景。
Here's a simple example:
这是一个简单的例子:
In this case, I map the height of the Authorlabel 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 Playbutton's height allows for it.
当我将约束的高度设置为 时0.0f
,我们保留了“填充”,因为播放按钮的高度允许它。
回答by Michael Peterson
What I ended up doing was creating 2 xibs. One with the left view and one without it. I registered both in the controller and then decided which to use during cellForRowAtIndexPath.
我最终做的是创建 2 个 xib。一个有左视图,一个没有它。我在控制器中注册了两个,然后决定在 cellForRowAtIndexPath 期间使用哪个。
They use the same UITableViewCell class. The downside is that there is some duplication of the content between the xibs, but these cells are pretty basic. The upside is that I don't have a bunch of code to manually manage removing view, updating constraints, etc.
它们使用相同的 UITableViewCell 类。缺点是xibs之间存在一些重复的内容,但这些单元格是非常基本的。好处是我没有一堆代码来手动管理删除视图、更新约束等。
In general, this is probably a better solution since they are technically different layouts and therefore should have different xibs.
一般来说,这可能是一个更好的解决方案,因为它们在技术上是不同的布局,因此应该有不同的 xib。
[self.table registerNib:[UINib nibWithNibName:@"TrackCell" bundle:nil] forCellReuseIdentifier:@"TrackCell"];
[self.table registerNib:[UINib nibWithNibName:@"TrackCellNoImage" bundle:nil] forCellReuseIdentifier:@"TrackCellNoImage"];
TrackCell *cell = [tableView dequeueReusableCellWithIdentifier:(appDelegate.showImages ? @"TrackCell" : @"TrackCellNoImage") forIndexPath:indexPath];
回答by n0c
connect constraint between uiview and labels as IBOutlet and set priority member to a less value when set hidden = YES
将 uiview 和标签之间的约束连接为 IBOutlet,并在设置 hidden = YES 时将优先级成员设置为较小的值
回答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;
回答by Roman Solodyashkin
Use two UIStackView Horizontal and Vertical, when some subview view in stack is hidden other stack subviews will be moved, use Distribution -> Fill Proporionally for Vertical stack with two UILabels and need set width and height constaints for first UIView
使用两个 UIStackView Horizontal 和 Vertical,当堆栈中的某些子视图被隐藏时,其他堆栈子视图将被移动,使用 Distribution -> Fill Proporionally for Vertical stack with两个 UILabels 并需要为第一个 UIView 设置宽度和高度约束