AutoLayout与隐藏的UIViews?

我觉得这是一个相当普遍的范例,根据业务逻辑显示/隐藏UIViews ,通常是UILabels 。 我的问题是,什么是最好的方式使用AutoLayout来响应隐藏的意见,如果他们的帧是0x0。 以下是1-3个function的dynamic列表示例。

动态功能列表

现在我从button到最后一个标签有一个10px的顶部空间,当隐藏标签时,它显然不会向上滑动。 到现在为止,我创build了这个约束的出口,并根据我显示的标签数量来修改常量。 这显然有点不好意思,因为我使用负常数值将button向上推到隐藏的帧上。 这也是不好的,因为它不受限于实际的布局元素,只是基于已知的高度/其他元素的填充的偷偷静态计算,显然与AutoLayout的构build。

我显然可以根据我的dynamic标签创build新的约束条件,但是这样做有很多的微观pipe理,并且为了试图压缩一些空白而需要大量的冗长。 有更好的方法吗? 改变帧大小0,0,让AutoLayout做它的事情,没有约束的操纵? 完全删除视图?

老实说,只是从隐藏视图的上下文修改常量需要一行代码和简单的计算。 使用constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:重新创build新约束constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:似乎很重。

我个人对显示/隐藏视图的偏好是创build一个具有适当宽度或高度约束的IBOutlet。

然后,我将constant数值更新为0来隐藏,或者任何值应该显示。

这种技术的一大优点是可以保持相对的约束。 例如,假设您有查看A并查看B的水平间距为x 。 当视图宽度constant设置为0.f视图B将向左移动以填充该空间。

没有必要添加或删除约束,这是一个重量级的操作。 简单地更新约束条件constant就可以实现。

当隐藏时使用常量0 ,而如果再次显示则使用另一个常量的解决scheme是有效的,但如果您的内容具有灵活的大小,则不能满足要求。 你需要测量你的灵活内容,并设置一个常数。 这感觉不对,并且由于服务器或UI事件而导致内容改变大小时会出现问题。

我有一个更好的解决scheme。

我们的想法是设置一个0高度规则,当我们隐藏元素的时候,它的优先级是高的,所以它不占用自动布局的空间。

以下是你如何做到这一点:

1.在界面生成器中设置一个宽度(或高度)为0的低优先级。

设置宽度0规则

Interface Builder不会因冲突而大喊大叫,因为优先级低。 通过暂时将优先级设置为999来testing高度行为(禁止1000以编程方式进行突变,所以我们不会使用它)。 接口构build器可能现在会大声说出冲突的约束。 您可以通过将相关对象的优先级设置为900左右来解决这些问题。

2.添加一个sockets,以便您可以在代码中修改宽度约束的优先级:

插座连接

3.隐藏元素时调整优先级:

 cell.alertTimingView.hidden = place.closingSoon != true cell.alertTimingWidth.priority = place.closingSoon == true ? 250 : 999 

UIStackView可能是iOS 9+的方法。 它不仅处理隐藏的视图,它还将删除额外的间距和边距,如果设置正确。

在这种情况下,我将作者标签的高度映射到适当的IBOutlet:

 @property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight; 

当我将约束的高度设置为0.0f时,我们保留“填充”,因为“播放”button的高度允许。

 cell.authorLabelHeight.constant = 0; //Hide cell.authorLabelHeight.constant = 44; //Show 

在这里输入图像描述

子视图并重写func intrinsicContentSize() -> CGSize 。 只要隐藏视图就返回CGSizeZero

我build立类别来轻松地更新约束:

 [myView1 hideByHeight:YES]; 

在这里回答: 隐藏自动布局UIView:如何获得现有的NSLayoutConstraint更新这一个

在这里输入图像描述

我很惊讶,没有一个更好的方法提供的UIKit这个所需的行为。 这似乎是一个非常普遍的事情,希望能够做到。

由于将约束连接到IBOutlets并将它们的常量设置为0感觉不太好(并且在视图有子视图时导致了NSLayoutConstraint警告),所以我决定创build一个扩展,给出一个简单的,有状态的方法来隐藏/显示具有自动布局约束的UIView

它只是隐藏了视图并消除了外部约束。 当您再次显示视图时,它会添加约束。 唯一需要注意的是,您需要为周围的视图指定灵活的故障转移限制。

编辑此答案针对iOS 8.4及以下版本。 在iOS 9中,只需使用UIStackView方法即可。

我只是发现要获得一个UILabel不占用空间,你必须隐藏它,并将其文本设置为一个空string。 (iOS 9)

知道这个事实/错误可以帮助一些人简化他们的布局,甚至可能是原来的问题,所以我想我会张贴它。

最好的做法是,一旦所有东西都有正确的布局约束,请根据希望周围的视图如何移动并将约束连接到IBOutlet属性来添加高度或约束。

确保你的属性是strong

在代码中哟只需将常量设置为0并激活它,隐藏内容,或停用它来显示内容。 这比用不变的价值来解决问题更好。 之后不要忘记调用layoutIfNeeded

如果将要隐藏的内容分组,则最佳做法是将所有内容放入视图并将约束添加到该视图

 @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]; } 

一旦你有你的设置,你可以在IntefaceBuilder中testing它,通过设置你的约束为0,然后回到原来的价值。 不要忘记检查其他的约束优先事项,所以隐藏时根本不存在冲突。 其他testing方法是将其设置为0并将优先级设置为0,但是,您不应该忘记将其恢复到最高优先级。

我也会提供我的解决scheme,以提供多样化。)我认为创build一个出口的每个项目的宽度/高度加上间距只是荒谬的,吹起的代码,可能的错误和并发症的数量。

我的方法删除所有视图(在我的情况下,UIImageView实例),select哪些需要添加回来,并在一个循环中,它添加回每个和创build新的约束。 其实很简单,请遵循。 这是我的快速和肮脏的代码来做到这一点:

 // remove all views [self.twitterImageView removeFromSuperview]; [self.localQuestionImageView removeFromSuperview]; // self.recipients always has to be present NSMutableArray *items; items = [@[self.recipients] mutableCopy]; // optionally add the twitter image if (self.question.sharedOnTwitter.boolValue) { [items addObject:self.twitterImageView]; } // optionally add the location image if (self.question.isLocal) { [items addObject:self.localQuestionImageView]; } UIView *previousItem; UIView *currentItem; previousItem = items[0]; [self.contentView addSubview:previousItem]; // now loop through, add the items and the constraints for (int i = 1; i < items.count; i++) { previousItem = items[i - 1]; currentItem = items[i]; [self.contentView addSubview:currentItem]; [currentItem mas_remakeConstraints:^(MASConstraintMaker *make) { make.centerY.equalTo(previousItem.mas_centerY); make.right.equalTo(previousItem.mas_left).offset(-5); }]; } // here I just connect the left-most UILabel to the last UIView in the list, whichever that was previousItem = items.lastObject; [self.userName mas_remakeConstraints:^(MASConstraintMaker *make) { make.right.equalTo(previousItem.mas_left); make.leading.equalTo(self.text.mas_leading); make.centerY.equalTo(self.attachmentIndicator.mas_centerY);; }]; 

我得到干净,一致的布局和间距。 我的代码使用砌体,我强烈build议: https : //github.com/SnapKit/Masonry

我喜欢的方法与Jorge Arimany提出的非常相似。

我更喜欢创build多个约束。 首先为第二个标签可见时创build约束。 为button和第二个标签之间的约束创build一个出口(如果你正在使用objc,确保其强大)。 此约束确定button和第二个标签之间的高度。

然后创build另一个约束,指定隐藏第二个button时button和顶部标签之间的高度。 创build一个出口到第二个约束,并确保这个sockets有一个强大的指针。 然后在接口生成器中取消选中已installedcheckbox,并确保第一个约束的优先级低于第二个约束优先级。

最后,当你隐藏第二个标签时,切换这些约束的.isActive属性并调用setNeedsDisplay()

就是这样,没有魔法数字,没有math,如果你有多个约束来打开和closures,你甚至可以使用outlet集合来保持它们按照状态组织。 (AKA将所有隐藏的约束保留在一个OutletCollection中,而非隐藏的约束保存在另一个OutletCollection中,只是迭代每个集合切换其.isActive状态)。

我知道Ryan Romanchuk说他不想使用多个约束,但是我觉得这不是微观pipe理,而且更简单的是以编程方式dynamic地创build视图和约束(这是我认为他想要避免的,如果我正确的阅读这个问题)。

我创build了一个简单的例子,我希望它是有用的…

 import UIKit class ViewController: UIViewController { @IBOutlet var ToBeHiddenLabel: UILabel! @IBOutlet var hiddenConstraint: NSLayoutConstraint! @IBOutlet var notHiddenConstraint: NSLayoutConstraint! @IBAction func HideMiddleButton(_ sender: Any) { ToBeHiddenLabel.isHidden = !ToBeHiddenLabel.isHidden notHiddenConstraint.isActive = !notHiddenConstraint.isActive hiddenConstraint.isActive = !hiddenConstraint.isActive self.view.setNeedsDisplay() } } 

在这里输入图像描述