如何调整superview以适应所有子视图与自动布局?

我对自动布局的理解是,它需要superview的大小,并基于约束和内在大小来计算子视图的位置。

有没有办法扭转这个过程? 我想根据约束和内在大小调整superview。 什么是实现这个最简单的方法?

我有在Xcodedevise的视图,我用它作为UITableView头。 这个视图包括一个标签和一个button。 标签的大小根据数据而不同。 根据约束条件,标签成功地按下button,或者如果在button和超级视图的底部之间存在约束,则标签被压缩。

我发现了一些类似的问题,但他们没有好的和简单的答案。

使用正确的API是UIView systemLayoutSizeFittingSize:传递UILayoutFittingCompressedSizeUILayoutFittingExpandedSize

对于使用自动布局的正常的UIView ,只要你的约束是正确的,这应该只是工作。 如果你想在UITableViewCell上使用它(例如确定行高),那么你应该把它叫做你的单元格contentView并抓取高度。

如果您的视图中有一个或多个UILabel是多行的,则还需要考虑其他因素。 对于这些,正确设置preferredMaxLayoutWidth属性是不够的,这样标签提供了一个正确的intrinsicContentSize ,它将在systemLayoutSizeFittingSize's计算中使用。

编辑:通过请求,添加一个表视图单元格的高度计算的例子

使用自动布局进行表格单元高度计算并不是非常高效,但确实很方便,特别是如果您的单元具有复杂的布局。

正如我上面所说,如果您使用多行UILabel则必须将preferredMaxLayoutWidth同步到标签宽度。 我使用一个自定义的UILabel子类来做到这一点:

 @implementation TSLabel - (void) layoutSubviews { [super layoutSubviews]; if ( self.numberOfLines == 0 ) { if ( self.preferredMaxLayoutWidth != self.frame.size.width ) { self.preferredMaxLayoutWidth = self.frame.size.width; [self setNeedsUpdateConstraints]; } } } - (CGSize) intrinsicContentSize { CGSize s = [super intrinsicContentSize]; if ( self.numberOfLines == 0 ) { // found out that sometimes intrinsicContentSize is 1pt too short! s.height += 1; } return s; } @end 

这是一个人为的UITableViewController子类,演示了heightForRowAtIndexPath:

 #import "TSTableViewController.h" #import "TSTableViewCell.h" @implementation TSTableViewController - (NSString*) cellText { return @"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; } #pragma mark - Table view data source - (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView { return 1; } - (NSInteger) tableView: (UITableView *)tableView numberOfRowsInSection: (NSInteger) section { return 1; } - (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath { static TSTableViewCell *sizingCell; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sizingCell = (TSTableViewCell*)[tableView dequeueReusableCellWithIdentifier: @"TSTableViewCell"]; }); // configure the cell sizingCell.text = self.cellText; // force layout [sizingCell setNeedsLayout]; [sizingCell layoutIfNeeded]; // get the fitting size CGSize s = [sizingCell.contentView systemLayoutSizeFittingSize: UILayoutFittingCompressedSize]; NSLog( @"fittingSize: %@", NSStringFromCGSize( s )); return s.height; } - (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath { TSTableViewCell *cell = (TSTableViewCell*)[tableView dequeueReusableCellWithIdentifier: @"TSTableViewCell" ]; cell.text = self.cellText; return cell; } @end 

简单的自定义单元格:

 #import "TSTableViewCell.h" #import "TSLabel.h" @implementation TSTableViewCell { IBOutlet TSLabel* _label; } - (void) setText: (NSString *) text { _label.text = text; } @end 

而且,这里是故事板中定义的约束图片。 请注意,标签上没有高度/宽度约束 – 这些约束是从标签的intrinsicContentSize中推断出来的:

在这里输入图像说明

埃里克·贝克(Eric Baker)的评论告诉我这样一个核心思想: 为了使视图的大小由其内容决定,那么放置在内容中的内容必须与包含视图有明确的关系以便驱动其高度(或宽度)dynamic 。 “添加子视图”不会像你想象的那样创build这种关系。 您必须select哪个子视图来驱动容器的高度和/或宽度…最常见的是您放置在整个UI右下angular的任何UI元素。 这里有一些代码和内联评论来说明这一点。

请注意,这对于那些使用滚动视图的人来说可能是特别有价值的,因为在一个单独的内容视图中devise它是很常见的,这个内容视图决定了它的大小(并且把它传递给滚动视图)。 祝你好运,希望这有助于有人在那里。

 // // ViewController.m // AutoLayoutDynamicVerticalContainerHeight // #import "ViewController.h" @interface ViewController () @property (strong, nonatomic) UIView *contentView; @property (strong, nonatomic) UILabel *myLabel; @property (strong, nonatomic) UILabel *myOtherLabel; @end @implementation ViewController - (void)viewDidLoad { // INVOKE SUPER [super viewDidLoad]; // INIT ALL REQUIRED UI ELEMENTS self.contentView = [[UIView alloc] init]; self.myLabel = [[UILabel alloc] init]; self.myOtherLabel = [[UILabel alloc] init]; NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_contentView, _myLabel, _myOtherLabel); // TURN AUTO LAYOUT ON FOR EACH ONE OF THEM self.contentView.translatesAutoresizingMaskIntoConstraints = NO; self.myLabel.translatesAutoresizingMaskIntoConstraints = NO; self.myOtherLabel.translatesAutoresizingMaskIntoConstraints = NO; // ESTABLISH VIEW HIERARCHY [self.view addSubview:self.contentView]; // View adds content view [self.contentView addSubview:self.myLabel]; // Content view adds my label (and all other UI... what's added here drives the container height (and width)) [self.contentView addSubview:self.myOtherLabel]; // LAYOUT // Layout CONTENT VIEW (Pinned to left, top. Note, it expects to get its vertical height (and horizontal width) dynamically based on whatever is placed within). // Note, if you don't want horizontal width to be driven by content, just pin left AND right to superview. [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_contentView]" options:0 metrics:0 views:viewsDictionary]]; // Only pinned to left, no horizontal width yet [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_contentView]" options:0 metrics:0 views:viewsDictionary]]; // Only pinned to top, no vertical height yet /* WHATEVER WE ADD NEXT NEEDS TO EXPLICITLY "PUSH OUT ON" THE CONTAINING CONTENT VIEW SO THAT OUR CONTENT DYNAMICALLY DETERMINES THE SIZE OF THE CONTAINING VIEW */ // ^To me this is what's weird... but okay once you understand... // Layout MY LABEL (Anchor to upper left with default margin, width and height are dynamic based on text, font, etc (ie UILabel has an intrinsicContentSize)) [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_myLabel]" options:0 metrics:0 views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[_myLabel]" options:0 metrics:0 views:viewsDictionary]]; // Layout MY OTHER LABEL (Anchored by vertical space to the sibling label that comes before it) // Note, this is the view that we are choosing to use to drive the height (and width) of our container... // The LAST "|" character is KEY, it's what drives the WIDTH of contentView (red color) [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_myOtherLabel]-|" options:0 metrics:0 views:viewsDictionary]]; // Again, the LAST "|" character is KEY, it's what drives the HEIGHT of contentView (red color) [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_myLabel]-[_myOtherLabel]-|" options:0 metrics:0 views:viewsDictionary]]; // COLOR VIEWS self.view.backgroundColor = [UIColor purpleColor]; self.contentView.backgroundColor = [UIColor redColor]; self.myLabel.backgroundColor = [UIColor orangeColor]; self.myOtherLabel.backgroundColor = [UIColor greenColor]; // CONFIGURE VIEWS // Configure MY LABEL self.myLabel.text = @"HELLO WORLD\nLine 2\nLine 3, yo"; self.myLabel.numberOfLines = 0; // Let it flow // Configure MY OTHER LABEL self.myOtherLabel.text = @"My OTHER label... This\nis the UI element I'm\narbitrarily choosing\nto drive the width and height\nof the container (the red view)"; self.myOtherLabel.numberOfLines = 0; self.myOtherLabel.font = [UIFont systemFontOfSize:21]; } @end 

如何调整superview,以适应与autolayout.png所有子视图

你可以通过创build一个约束并通过接口构build器连接来完成

请参阅说明: Auto_Layout_Constraints_in_Interface_Builder

raywenderlich开始自动布局

AutolayoutPG文章约束基础

 @interface ViewController : UIViewController { IBOutlet NSLayoutConstraint *leadingSpaceConstraint; IBOutlet NSLayoutConstraint *topSpaceConstraint; } @property (weak, nonatomic) IBOutlet NSLayoutConstraint *leadingSpaceConstraint; 

连接这个约束sockets与你的子视图约束或连接超级视图约束,并根据你的要求这样设置

  self.leadingSpaceConstraint.constant = 10.0;//whatever you want to assign 

我希望这个澄清。

这可以在一个较大的UIView内的一个正常的subview ,但它不会自动为headerViews工作。 headerView的高度由tableView:heightForHeaderInSection:返回的内容确定tableView:heightForHeaderInSection:所以你必须根据UIButtonUILabel加上空间的height和你需要的任何padding来计算height 。 你需要做这样的事情:

 -(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { NSString *s = self.headeString[indexPath.section]; CGSize size = [s sizeWithFont:[UIFont systemFontOfSize:17] constrainedToSize:CGSizeMake(281, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping]; return size.height + 60; } 

这里的headerString是你想要填充UILabel任何string,而281号码是UILabelwidth (如Interface Builder设置)