我已经在IB中设计了自定义单元,将其子类化,然后将插座连接到我的自定义类。我在单元格内容中有三个子视图:UIView(cdView)和两个标签(titleLabel和emailLabel)。根据每行可用的数据,有时我想在单元格中显示UIView和两个标签,有时只显示两个标签。我要尝试做的是这样设置约束:如果我将UIView属性设置为hidden,或者将其从超级视图中删除,则两个标签将向左移动。我试图将UIView领先约束设置为10像素的Superview(单元格内容),并将UILabels领先约束10像素的像素设置为下一个视图(UIView)。稍后在我的代码中

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(IndexPath *)indexPath {
...
Record *record = [self.records objectAtIndex:indexPath.row];

if ([record.imageURL is equalToString:@""]) {
     cell.cdView.hidden = YES;
}


我隐藏了cell.cdView,我希望标签向左移动,但是它们在Cell中的位置相同。我试图从超级视图中删除cell.cdView,但它也不起作用。我已附上图片以阐明我的意思。



我知道如何以编程方式执行此操作,因此我没有寻找该解决方案。我想要在IB中设置约束,并且我希望如果其他视图被删除或隐藏,我的子视图将动态移动。

.....


可以在IB中使用自动布局吗?

评论

更改约束值运行时-检查此答案

对于这种特定情况,您还可以使用UIStackView。隐藏CD时,标签将占据其空间

#1 楼

有可能,但是您需要做一些额外的工作。首先需要解决一些概念上的问题:


隐藏的视图,即使它们不绘制,仍会参与自动版式并通常保留其框架,而其他相关视图在其位置中。
从其超级视图中删除视图时,所有相关约束也会从该视图层次结构中删除。

对于您而言,这可能意味着:


如果将左视图设置为隐藏,标签将保留在原处,因为该左视图仍会占用空间(即使它不可见)。
如果删除左视图,您的标签可能会受到含糊的约束,因为您不再对标签的左边缘有约束。

明智地过度约束标签。保留现有的约束(到另一个视图的空间为10pts),但添加另一个约束:以不需要的优先级使标签的左边缘距其父视图的左边缘10pts(默认的高优先级可能会很好地工作)。 br />
然后,当您希望它们向左移动时,请完全删除左视图。左侧视图的强制性10pt约束将与它所关联的视图一起消失,而您将只剩下一个高优先级约束,即标签距离其父视图至少10pts。在下一次布局传递时,这应该使它们向左扩展,直到它们填满超级视图的宽度为止,但要保证您在边缘周围的间距。

一个重要的警告:如果您想让左视图返回图片,不仅需要将其重新添加到视图层次结构中,而且还必须同时重新建立其所有约束。这意味着每当再次显示该视图时,您都需要一种在视图及其标签之间放回10pt间距约束的方法。

评论


尽管此答案肯定会奏效,但IMO过度处理各种用例似乎是一种代码味道-尤其是因为您必须为要再次显示该视图的所有已删除视图重新建立所有约束。

–备忘录
13年10月10日在4:41

我认为这不是要走的路。您应该对要隐藏的视图使用宽度/高度约束。

–ullstrm
2014年2月4日在10:19

我谨不同意。例如,如果将视图的宽度设置为0,则会遇到两个问题。首先,现在在超级视图和可见视图之间具有双倍间距:|-(空格)-[hidden(0)]-(空格)-[可见]实际上是|-(2 *空格)-[可见]。其次,该视图可能开始抛出违反约束的情况,具体取决于其自身的视图子树和约束-您无法保证可以将视图任意限制为0宽度并使其继续工作。

– Tim
2014年2月4日在18:12

如果您使用的是具有固有内容大小的视图,那么Tim的答案似乎是唯一的解决方法。

– Balkoth
2014年5月27日16:06



谢谢蒂姆。我将优先级设置为0以避免约束不兼容,但是现在我意识到双间距存在问题。我没有那个问题,因为我从来没有同时显示两个视图(我的情况:|-[otherViews]-[要么This] [orThis]-|),但是最终我会遇到这个问题。

– Ferran Maylinch
15年4月8日在9:26

#2 楼

在运行时添加或删除约束是一项重量级的操作,可能会影响性能。但是,有一个更简单的选择。

对于要隐藏的视图,设置宽度约束。要隐藏其他视图,并在该视图之间添加前导水平间隙。

要隐藏,请将宽度约束的.constant更新为0.f。其他视图将自动向左移动到位置。

有关更多详细信息,请参见此处的其他答案:

如何在运行时更改标签约束?

评论


此解决方案的唯一问题是,左边距将是您可能想要的两倍,因此我也将更新这些约束之一,但是即使那样,我仍然认为这比删除子视图要少。

–JoséManuelSánchez
2014年1月17日上午10:47

@ skinsfan00atg如果使用的是具有固有内容大小的视图,则不能使用此解决方案。

– Balkoth
14年5月27日在16:09

@balkoth为什么不呢?您可以减少内容优先级

– Max MacLeod
2014年5月27日在17:29

@MaxMacLeod如果减少内容包含优先级,则您未使用固有内容大小,而是使用了约束指示的大小。

– Balkoth
2014年5月29日在9:33

@MaxMacLeod好的,我明白您的意思了。当您要隐藏视图并再次显示该视图时,需要在代码中将抗压缩优先级设置为0(而不是内容拥抱)。除此之外,您还需要在接口构建器中添加一个约束,以将视图的大小设置为0。无需在代码中触摸此约束。

– Balkoth
2014年5月29日10:27



#3 楼

对于仅支持iOS 8+的用户,有一个新的布尔属性处于活动状态。这将有助于动态地仅启用所需的约束

P.S.约束出口必须强而不弱。
示例:

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


此外,要修复情节提要中的错误,您需要取消选中以下一项的Installed复选框约束。
UIStackView(iOS 9+)

还有一个选择是将视图包装在UIStackView中。隐藏视图后,UIStackView将自动更新布局

评论


活动部分起作用。但是,如果我在可重用的单元格中使用它,则从激活到停用都可以工作,但不能从停用到激活。有任何想法吗?还是可以提供一个例子?

–李A
15年11月13日在16:43

发生这种情况是因为未加载或取消分配了停用的约束。您的约束出口应该强而不弱

– Silmaril
15年11月16日在9:33

我们在哪里可以找到“活动”财产?

–拉克希米·雷迪(Lakshmi Reddy)
2015年11月20日7:09

developer.apple.com/library/ios/documentation/AppKit/Reference/…

– Silmaril
15年11月20日在12:08

似乎过度约束+设置约束活动可能是最合理的答案。

–丁一诗
18-10-12在5:34

#4 楼

UIStackView属性的任何子视图(iOS 9+)更改时,hidden都会自动重新定位其视图。

UIView.animateWithDuration(1.0) { () -> Void in
   self.mySubview.hidden = !self.mySubview.hidden
}


在此WWDC视频中跳至11:48进行演示:

自动布局的奥秘,第1部分

评论


我隐藏了一个嵌套的堆栈视图,并且整个包含堆栈的视图都消失了。

–伊恩·沃伯顿(Ian Warburton)
16年11月29日在19:18

这应该是公认的答案。遵循wwdc 2015的苹果要求,在界面构建器设计上。

– Thibaut Noah
17年5月12日在8:47

@thibautnoah那么iOS 8支持呢?

–行李
17年9月29日在12:41

@bagage除非您是facebook或google,否则可以将其删除。借助iOS 9的覆盖范围,您将支持90%以上的设备,绰绰有余。以下支持将削弱您的开发流程,并阻止您使用imo最新功能。

– Thibaut Noah
17年9月29日在12:57

#5 楼

我的项目使用@IBDesignable的自定义UILabel子类(以确保颜色,字体,插图等的一致性),并且实现了以下内容:

override func intrinsicContentSize() -> CGSize {
    if hidden {
        return CGSizeZero
    } else {
        return super.intrinsicContentSize()
    }
}


这允许标签子类可以参与“自动布局”,但隐藏时不占空间。

#6 楼

对于Google员工:以Max的答案为基础,为了解决许多人已经注意到的填充问题,我只是增加了标签的高度,并使用该高度作为分隔符,而不是实际的填充。这个想法可以扩展到任何包含视图的场景。这里是一个简单的示例:



在这种情况下,我绘制了高度标签到适当的IBOutlet的位置:

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

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

评论


对于那些不熟悉NSLayoutConstraint的人,我相信您想更新authorLabelHeight的constant属性。

–凯尔
16-2-29在15:56

#7 楼

将uiview和标签之间的约束连接为IBOutlet,并在设置hidden = YES
时将优先级成员设置为较小的值

评论


建立NSLayoutConstraint后,您将无法调整其优先级;您必须删除并读取具有不同优先级的新约束。

– Tim
2014年2月4日在18:14

我已经使用了这种方法。我有一个使用标签和按钮的包装盒,需要隐藏按钮并展开标签。我有两个约束,一个约束最初具有751优先级,另一个约束具有750。然后,当我隐藏按钮时,我翻转了优先级,并且标签的长度增加了。应该注意的是,如果尝试将更高的优先级设置为1000,则会出现错误“不支持将优先级从必需的更改为未安装的约束(反之亦然)”。所以不要,您似乎还好。 Xcode 5.1 / viewDidLoad。

–约翰·布什内尔
2014年3月26日在22:14

#8 楼

我最终要做的是创建2个xib。一种带有左视图,另一种没有左视图。我在控制器中都进行了注册,然后决定在cellForRowAtIndexPath中使用哪个。

它们使用相同的UITableViewCell类。缺点是xib之间的内容有些重复,但是这些单元格非常基础。好处是我没有一堆代码来手动管理删除视图,更新约束等。

一般来说,这可能是一个更好的解决方案,因为它们在技术上是不同的布局,因此应该有不同的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];


#9 楼

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

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


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

cell.authorLabelHeight.constant = 0;





#10 楼

使用两个UIStackView水平和垂直,当堆栈中的某个子视图视图被隐藏时,其他堆栈子视图将被移动,对两个带有UILabel的垂直堆栈使用Distribution-> Proporionally Fill,并且需要为第一个UIView设置宽度和高度约束

#11 楼

在我的情况下,我将高度约束的常量设置为0.0f,还将hidden属性设置为YES。要再次显示视图(带有子视图),我做了相反的事情:我设置了高度常量设置为非零值,并将hidden属性设置为NO

#12 楼

试试这个,我已经实现了下面的代码,

我在ViewController上有一个视图,其中添加了另外三个视图,当任何一个视图被隐藏时,另外两个视图将移动,请按照以下步骤操作。


1.ViewController.h文件

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *viewOne;
@property (strong, nonatomic) IBOutlet UIView *viewTwo;
@property (strong, nonatomic) IBOutlet UIView *viewThree;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewOneWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewTwoWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewThreeWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewBottomWidth;
@end


2.ViewController.m

 #import "ViewController.h"
 @interface ViewController ()
{
  CGFloat viewOneWidthConstant;
  CGFloat viewTwoWidthConstant;
  CGFloat viewThreeWidthConstant;
  CGFloat viewBottomWidthConstant;
}
@end

@implementation ViewController
@synthesize viewOne, viewTwo, viewThree;

- (void)viewDidLoad {
  [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a 
  nib.

  /*
   0  0   0
   0  0   1
   0  1   0
   0  1   1
   1  0   0
   1  0   1
   1  1   0
   1  1   1
   */

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:YES];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:YES];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:YES];
  //    [viewThree setHidden:YES];


  //    [viewOne setHidden:YES];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:YES];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:YES];

 //    [viewOne setHidden:YES];
 //    [viewTwo setHidden:YES];
 //    [viewThree setHidden:NO];

//    [viewOne setHidden:YES];
//    [viewTwo setHidden:YES];
//    [viewThree setHidden:YES];

 [self hideShowBottomBar];
  }

- (void)hideShowBottomBar
{
  BOOL isOne = !viewOne.isHidden;
  BOOL isTwo = !viewTwo.isHidden;
  BOOL isThree = !viewThree.isHidden;

  viewOneWidthConstant = _viewOneWidth.constant;
  viewTwoWidthConstant = _viewTwoWidth.constant;
  viewThreeWidthConstant = _viewThreeWidth.constant;
  viewBottomWidthConstant = _viewBottomWidth.constant;

   if (isOne && isTwo && isThree) {
    // 0    0   0
    _viewOneWidth.constant = viewBottomWidthConstant / 3;
    _viewTwoWidth.constant = viewBottomWidthConstant / 3;
    _viewThreeWidth.constant = viewBottomWidthConstant / 3;
    }
    else if (isOne && isTwo && !isThree) {
     // 0    0   1
    _viewOneWidth.constant = viewBottomWidthConstant / 2;
    _viewTwoWidth.constant = viewBottomWidthConstant / 2;
    _viewThreeWidth.constant = 0;
    }
   else if (isOne && !isTwo && isThree) {
    // 0    1   0
    _viewOneWidth.constant = viewBottomWidthConstant / 2;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = viewBottomWidthConstant / 2;
    }
    else if (isOne && !isTwo && !isThree) {
    // 0    1   1
    _viewOneWidth.constant = viewBottomWidthConstant;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = 0;
   }
   else if (!isOne && isTwo && isThree) {
    // 1    0   0
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = viewBottomWidthConstant / 2;
    _viewThreeWidth.constant = viewBottomWidthConstant / 2;
   }
   else if (!isOne && isTwo && !isThree) {
    // 1    0   1
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = viewBottomWidthConstant;
    _viewThreeWidth.constant = 0;
   }
   else if (!isOne && !isTwo && isThree) {
    // 1    1   0
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = viewBottomWidthConstant;
   }
   else if (isOne && isTwo && isThree) {
    // 1    1   1
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = 0;
   }
  }

 - (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
 // Dispose of any resources that can be recreated.
 }
 @end






希望如此,这种逻辑会有所帮助。

#13 楼

我将使用水平stackview。当子视图隐藏时,它可以删除框架。

在下图中,红色视图是您内容的实际容器,橙色超级视图(ShowHideView)的尾部空间为10pt,然后将ShowHideView连接到IBOutlet并以编程方式显示/隐藏/删除它。 br />

这是可见/已安装视图的情况。




这是隐藏视图/的情况未安装。



#14 楼

这是我使用优先级约束的另一种解决方案。这个想法是将宽度设置为0。


创建容器视图(橙色)并设置宽度。

创建内容视图(红色)并设置尾随空间10pt超级观看(橙色)。注意尾随空间约束,有两个优先级不同的尾随约束。低(= 10)和高(<= 10)。这对于避免歧义很重要。

将橙色视图的宽度设置为0以隐藏视图。



#15 楼

只需使用UIStackView,一切都会正常。
无需担心其他约束,UIStackView将自动处理空间。

#16 楼

正如no_scene所建议的,您绝对可以通过在运行时更改约束的优先级来做到这一点。这对我来说要容易得多,因为我有一个以上的视图必须删除。

这是使用ReactiveCocoa的一个片段:

RACSignal* isViewOneHiddenSignal = RACObserve(self.viewModel, isViewOneHidden);
RACSignal* isViewTwoHiddenSignal = RACObserve(self.viewModel, isViewTwoHidden);
RACSignal* isViewThreeHiddenSignal = RACObserve(self.viewModel, isViewThreeHidden);
RAC(self.viewOne, hidden) = isViewOneHiddenSignal;
RAC(self.viewTwo, hidden) = isViewTwoHiddenSignal;
RAC(self.viewThree, hidden) = isViewThreeHiddenSignal;

RAC(self.viewFourBottomConstraint, priority) = [[[[RACSignal
    combineLatest:@[isViewOneHiddenSignal,
                    isViewTwoHiddenSignal,
                    isViewThreeHiddenSignal]]
    and]
    distinctUntilChanged]
    map:^id(NSNumber* allAreHidden) {
        return [allAreHidden boolValue] ? @(780) : @(UILayoutPriorityDefaultHigh);
    }];

RACSignal* updateFramesSignal = [RACObserve(self.viewFourBottomConstraint, priority) distinctUntilChanged];
[updateFramesSignal
    subscribeNext:^(id x) {
        @strongify(self);
        [self.view setNeedsUpdateConstraints];
        [UIView animateWithDuration:0.3 animations:^{
            [self.view layoutIfNeeded];
        }];
    }];


#17 楼

万一这对某人有帮助,我建立了一个使用视觉格式约束的帮助器类。我正在当前的应用程序中使用它。

AutolayoutHelper

它可能是针对我的需求量身定制的,但是您可能会发现它很有用,或者可能想对其进行修改并创建自己的帮助器。

我必须感谢Tim在上面的回答,关于UIScrollView的回答以及本教程。

#18 楼

我将按照以下方法重新调整uiview以获得您的解决方案:


拖放一个UIImageView并将其放置在左侧。
拖放一个UIView并将其放置在UIImageView的右侧。
在该UIView中拖放两个UILabel,其前,后约束均为零。
将包含2个标签的UIView的前导约束设置为Superview而不是UIImagView。
如果UIImageView隐藏,则设置前导约束常数为10像素以进行超级视图。否则,将前导约束常量设置为10 px + UIImageView.width + 10 px。

我创建了自己的拇指规则。每当您必须隐藏/显示其约束可能受到影响的任何uiview时,请将所有受影响的/依赖子视图添加到uiview中,并以编程方式更新其前导/尾随/顶部/底部约束常量。

#19 楼

这是一个古老的问题,但我仍然希望它会有所帮助。来自Android,在此平台上,您可以使用方便的方法isVisible将其从视图中隐藏,但在自动布局绘制视图时也无需考虑框架。

使用扩展和“扩展” uiview您可以在ios中执行类似的功能(不知道为什么它不在UIKit中)在swift 3中实现:

    func isVisible(_ isVisible: Bool) {
        self.isHidden = !isVisible
        self.translatesAutoresizingMaskIntoConstraints = isVisible
        if isVisible { //if visible we remove the hight constraint 
            if let constraint = (self.constraints.filter{q4312078q.firstAttribute == .height}.first){
                self.removeConstraint(constraint)
            }
        } else { //if not visible we add a constraint to force the view to have a hight set to 0
            let height = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal , toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 0)
            self.addConstraint(height)
        }
        self.layoutIfNeeded()
    }


#20 楼

正确的方法是使用isActive = false禁用约束。但是请注意,停用约束会删除并释放约束,因此您必须拥有强大的出口。

#21 楼

最简单的解决方案是使用UIStackView(水平)。添加到堆栈视图:带标签的第一个视图和第二个视图。
然后将第一个视图的isHidden属性设置为false。
将计算所有约束并自动更新。

#22 楼

我认为这是最简单的答案。请验证其是否有效:

        StackFullView.layer.isHidden = true
        Task_TopSpaceSections.constant = 0.   //your constraint of top view


在此处检查https://www.youtube.com/watch?v=EBulMWMoFuw

#23 楼

要隐藏UIView而不是隐藏视图,请创建宽度约束并在代码中将其更改为0。
这可能是最简单的方法。另外,它将保留视图,并且如果您想再次显示它,则无需重新创建它(理想的是在表单元格内部使用)。要更改常数值,您需要创建一个常数参考出口(与视图的出口相同)。