ios 如何调整超级视图的大小以适应所有具有自动布局的子视图?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18118021/
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 resize superview to fit all subviews with autolayout?
提问by DAK
My understanding of autolayout is that it takes the size of superview and base on constrains and intrinsic sizes it calculates positions of subviews.
我对自动布局的理解是,它采用超视图的大小,并根据约束和内在大小计算子视图的位置。
Is there a way to reverse this process? I want to resize superview on the base of constrains and intrinsic sizes. What is the simplest way of achieving this?
有没有办法扭转这个过程?我想根据约束和内在大小调整超级视图的大小。实现这一目标的最简单方法是什么?
I have view designed in Xcode which I use as a header for UITableView
. This view includes a label and a button. Size of the label differs depending on data. Depending on constrains the label successfully pushes the button down or if there is a constrain between the button and bottom of superview the label is compressed.
我有在 Xcode 中设计的视图,我将其用作UITableView
. 该视图包括一个标签和一个按钮。标签的大小因数据而异。根据约束,标签成功按下按钮,或者如果按钮和超级视图底部之间存在约束,则标签被压缩。
I have found a few similar questions but they don't have good and easy answers.
我发现了一些类似的问题,但他们没有很好和简单的答案。
回答by TomSwift
The correct API to use is UIView systemLayoutSizeFittingSize:
, passing either UILayoutFittingCompressedSize
or UILayoutFittingExpandedSize
.
要使用的正确 API 是UIView systemLayoutSizeFittingSize:
,传递UILayoutFittingCompressedSize
或UILayoutFittingExpandedSize
。
For a normal UIView
using autolayout this should just work as long as your constraints are correct. If you want to use it on a UITableViewCell
(to determine row height for example) then you should call it against your cell contentView
and grab the height.
对于正常UIView
使用自动布局,只要您的约束是正确的,它就应该起作用。如果你想在一个UITableViewCell
(例如确定行高)上使用它,那么你应该在你的单元格上调用它contentView
并获取高度。
Further considerations exist if you have one or more UILabel's in your view that are multiline. For these it is imperitive that the preferredMaxLayoutWidth
property be set correctly such that the label provides a correct intrinsicContentSize
, which will be used in systemLayoutSizeFittingSize's
calculation.
如果您的视图中有一个或多个 UILabel 是多行的,则需要进一步考虑。对于这些,preferredMaxLayoutWidth
必须正确设置属性,以便标签提供正确的intrinsicContentSize
,这将用于systemLayoutSizeFittingSize's
计算。
EDIT: by request, adding example of height calculation for a table view cell
编辑:根据要求,添加表格视图单元格的高度计算示例
Using autolayout for table-cell height calculation isn't super efficient but it sure is convenient, especially if you have a cell that has a complex layout.
使用自动布局进行表格单元格高度计算并不是非常有效,但它确实很方便,特别是如果您有一个具有复杂布局的单元格。
As I said above, if you're using a multiline UILabel
it's imperative to sync the preferredMaxLayoutWidth
to the label width. I use a custom UILabel
subclass to do this:
正如我上面所说,如果您使用的是多行,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
Here's a contrived UITableViewController subclass demonstrating heightForRowAtIndexPath:
这是一个人为的 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
A simple custom cell:
一个简单的自定义单元格:
#import "TSTableViewCell.h"
#import "TSLabel.h"
@implementation TSTableViewCell
{
IBOutlet TSLabel* _label;
}
- (void) setText: (NSString *) text
{
_label.text = text;
}
@end
And, here's a picture of the constraints defined in the Storyboard. Note that there are no height/width constraints on the label - those are inferred from the label's intrinsicContentSize
:
而且,这是 Storyboard 中定义的约束的图片。请注意,标签上没有高度/宽度限制 - 这些是从标签的 推断出来的intrinsicContentSize
:
回答by John Erck
Eric Baker's comment tipped me off to the core idea that in order for a view to have its size be determined by the content placed within it, then the content placed within it must have an explicit relationship with the containing view in order to drive its height (or width) dynamically. "Add subview" does not create this relationship as you might assume. You have to choose which subview is going to drive the height and/or width of the container... most commonly whatever UI element you have placed in the lower right hand corner of your overall UI. Here's some code and inline comments to illustrate the point.
Eric Baker 的评论让我明白了一个核心思想,为了让视图的大小由放置在其中的内容决定,放置在其中的内容必须与包含的视图有明确的关系以驱动其高度(或宽度)动态。“添加子视图”不会像您想象的那样创建这种关系。您必须选择哪个子视图将驱动容器的高度和/或宽度......最常见的是您放置在整个 UI 右下角的任何 UI 元素。这里有一些代码和内联注释来说明这一点。
Note, this may be of particular value to those working with scroll views since it's common to design around a single content view that determines its size (and communicates this to the scroll view) dynamically based on whatever you put in it. Good luck, hope this helps somebody out there.
请注意,这对于那些使用滚动视图的人来说可能具有特殊的价值,因为围绕单个内容视图进行设计是很常见的,该视图根据您放入的内容动态地确定其大小(并将其传达给滚动视图)。祝你好运,希望这可以帮助那里的人。
//
// 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 (i.e. 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
回答by chandan
You can do this by creating a constraint and connecting it via interface builder
您可以通过创建约束并通过界面构建器连接来做到这一点
See explanation: Auto_Layout_Constraints_in_Interface_Builder
见解释:Auto_Layout_Constraints_in_Interface_Builder
raywenderlich beginning-auto-layout
AutolayoutPG Articles constraint Fundamentals
@interface ViewController : UIViewController {
IBOutlet NSLayoutConstraint *leadingSpaceConstraint;
IBOutlet NSLayoutConstraint *topSpaceConstraint;
}
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *leadingSpaceConstraint;
connect this Constraint outlet with your sub views Constraint or connect super views Constraint too and set it according to your requirements like this
将此 Constraint 出口与您的子视图 Constraint 或连接超级视图 Constraint 并根据您的要求进行设置,如下所示
self.leadingSpaceConstraint.constant = 10.0;//whatever you want to assign
I hope this clarifies it.
我希望这能澄清它。
回答by rdelmar
This can be done for a normal subview
inside a larger UIView
, but it doesn't work automatically for headerViews
. The height of a headerView
is determined by what's returned by tableView:heightForHeaderInSection:
so you have to calculate the height
based on the height
of the UILabel
plus space for the UIButton
and any padding
you need. You need to do something like this:
这可以subview
在更大UIView
的headerViews
. a 的高度headerView
由返回的内容决定,tableView:heightForHeaderInSection:
因此您必须height
根据 a和任何您需要height
的UILabel
加空间来计算 a 。你需要做这样的事情:UIButton
padding
-(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;
}
Here headerString
is whatever string you want to populate the UILabel
, and the 281 number is the width
of the UILabel
(as setup in Interface Builder
)
这headerString
是您要填充 的任何字符串UILabel
,而 281 数字是width
的UILabel
(如 中的设置Interface Builder
)