Ios 具有隐藏视图的自动布局?

Ios 具有隐藏视图的自动布局?,ios,objective-c,autolayout,Ios,Objective C,Autolayout,我觉得根据业务逻辑显示/隐藏ui视图,通常是ui标签,这是一种相当常见的范例。我的问题是,使用AutoLayout响应隐藏视图的最佳方式是什么,就好像它们的帧是0x0一样。下面是1-3个功能的动态列表示例 现在我有一个10px的顶部空间,从按钮到最后一个标签,当标签被隐藏时,它显然不会向上滑动。从现在起,我创建了此约束的出口,并根据显示的标签数量修改常量。这显然有点老套,因为我使用负常量值在隐藏帧上按下按钮。这也很糟糕,因为它不受实际布局元素的约束,只是根据其他元素的已知高度/填充进行偷偷的静

我觉得根据业务逻辑显示/隐藏
ui视图
,通常是
ui标签
,这是一种相当常见的范例。我的问题是,使用AutoLayout响应隐藏视图的最佳方式是什么,就好像它们的帧是0x0一样。下面是1-3个功能的动态列表示例

现在我有一个10px的顶部空间,从按钮到最后一个标签,当标签被隐藏时,它显然不会向上滑动。从现在起,我创建了此约束的出口,并根据显示的标签数量修改常量。这显然有点老套,因为我使用负常量值在隐藏帧上按下按钮。这也很糟糕,因为它不受实际布局元素的约束,只是根据其他元素的已知高度/填充进行偷偷的静态计算,显然是在与AutoLayout的用途作斗争

很明显,我可以根据我的动态标签创建新的约束,但这需要大量的微观管理和大量的冗长,因为我试图折叠一些空白。有更好的方法吗?更改帧大小0,0并让AutoLayout在不操纵约束的情况下完成其工作?完全删除视图

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


编辑2018年2月:查看Ben关于
UIStackView
s

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

然后,我将
常量
值更新为
0
以隐藏,或者更新任何应该显示的值

这种技术的最大优点是可以保持相对约束。例如,假设视图A和视图B的水平间隙为x。当视图A宽度
常数
设置为
0.f
时,视图B将向左移动以填充该空间


不需要添加或删除约束,这是一个重量级操作。只需更新约束的
常量
就可以了。

我构建类别以轻松更新约束:

[myView1 hideByHeight:YES];
请回答:


我感到惊讶的是,
UIKit
没有为这种期望的行为提供更优雅的方法。这似乎是一件很平常的事情,希望能够做到

由于将约束连接到
IBOutlets
并将其常量设置为
0
让人感觉不舒服(并且在视图有子视图时导致
NSLayoutConstraint
警告),因此我决定创建一个扩展来提供

它仅隐藏视图并删除外部约束。再次显示视图时,会将约束添加回视图。唯一需要注意的是,您需要为周围的视图指定灵活的故障切换约束

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

隐藏时使用常量
0
,再次显示时使用另一个常量的解决方案是有效的,但如果内容大小灵活,则无法令人满意。您需要测量您的灵活内容,并设置一个常数。这感觉是错误的,如果内容因服务器或UI事件而改变大小,则会出现问题

我有一个更好的解决办法

其思想是在隐藏元素时,将0高度规则设置为具有高优先级,以便它不占用自动布局空间

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

1。在interface builder中以低优先级设置宽度(或高度)0。

Interface Builder不会大声抱怨冲突,因为优先级较低。通过将优先级临时设置为999来测试高度行为(1000禁止以编程方式进行变异,因此我们不会使用它)。Interface builder现在可能会大喊冲突约束。您可以通过将相关对象的优先级设置为900左右来修复这些问题

2。添加一个插座,以便在代码中修改宽度约束的优先级:

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

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

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

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

对视图进行子类化,并覆盖
func intrinsicContentSize()->CGSize
。如果视图是隐藏的,只需返回
CGSizeZero

我也会提供我的解决方案,以提供多样性。)我认为为每个项目的宽度/高度加上间距创建一个出口是荒谬的,会破坏代码、可能的错误和复杂性

我的方法删除所有视图(在我的例子中是UIImageView实例),选择需要添加回的视图,并在循环中添加回每个视图并创建新的约束。这其实很简单,请坚持到底。下面是我快速而肮脏的代码:

// 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);;
}];
//删除所有视图
[self.twitterImageView从SuperView移除];
[self.localQuestionImageView从SuperView移除];
//自助接受者必须始终在场
NSMutableArray*项;
items=[@[self.recipients]mutableCopy];
//可以选择添加twitter图像
if(self.question.sharedOnTwitter.boolValue){
[items addObject:self.twitterImageView];
}
//(可选)添加位置图像
if(self.question.isLocal){
[items addObject:self.localQuestionImageView];
}
UIView*上一项;
UIView*当前项目;
previousItem=项目[0];
[self.contentView addSubview:previousItem];
//现在循环,添加项目和约束
对于(int i=1;icell.authorLabelHeight.constant = 0; //Hide 
cell.authorLabelHeight.constant = 44; //Show
@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];
}
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()
    }
}
    boxView.optItems = [
        firstLabel.boxed.useIf(isFirstLabelShown),
        secondLabel.boxed.useIf(isSecondLabelShown),
        button.boxed
    ]