我对自动布局的理解是,它采用了超级视图的大小,并基于约束和内在大小来计算子视图的位置。
有没有办法扭转这个过程?我想根据约束和内在大小调整Superview的大小。最简单的方法是什么?
我在Xcode中设计了视图,并将其用作的标题UITableView
。该视图包括标签和按钮。标签的大小取决于数据。根据约束,标签可以成功按下按钮,或者如果按钮和超级视图底部之间存在约束,则标签将被压缩。
我发现了一些类似的问题,但没有好的和简单的答案。
我对自动布局的理解是,它采用了超级视图的大小,并基于约束和内在大小来计算子视图的位置。
有没有办法扭转这个过程?我想根据约束和内在大小调整Superview的大小。最简单的方法是什么?
我在Xcode中设计了视图,并将其用作的标题UITableView
。该视图包括标签和按钮。标签的大小取决于数据。根据约束,标签可以成功按下按钮,或者如果按钮和超级视图底部之间存在约束,则标签将被压缩。
我发现了一些类似的问题,但没有好的和简单的答案。
Answers:
正确使用的API UIView systemLayoutSizeFittingSize:
是UILayoutFittingCompressedSize
或传递UILayoutFittingExpandedSize
。
对于正常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
:
sizingCell
或的大小为非零contentView
。
sizingCell
的宽度与s的宽度匹配tableView
。
埃里克·贝克(Eric Baker)的评论向我传达了一个核心思想,即为了使视图的大小由放置在其中的内容确定,那么放置在其中的内容必须与包含视图具有显式关系才能驱动其高度(或宽度)动态。“添加子视图”不会创建您可能假定的关系。您必须选择哪个子视图来驱动容器的高度和/或宽度...最常见的是,您在整个UI右下角放置的UI元素。这是一些代码和内联注释来说明这一点。
请注意,这对于使用滚动视图的用户可能特别有价值,因为通常围绕一个内容视图进行设计,该内容视图根据您放置的内容动态确定其大小(并将其传达给滚动视图)。祝您好运,希望这对您有所帮助。
//
// 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
您可以通过创建约束并通过接口构建器将其连接来实现
参见说明:Auto_Layout_Constraints_in_Interface_Builder
@interface ViewController : UIViewController {
IBOutlet NSLayoutConstraint *leadingSpaceConstraint;
IBOutlet NSLayoutConstraint *topSpaceConstraint;
}
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *leadingSpaceConstraint;
将此约束出口与您的子视图约束连接,或者也可以将超级视图约束连接,并根据您的要求进行设置,例如
self.leadingSpaceConstraint.constant = 10.0;//whatever you want to assign
我希望这可以澄清它。
可以subview
在较大的内部进行正常处理UIView
,但是不能自动进行headerViews
。的高度headerView
是由什么是由归国决定tableView:heightForHeaderInSection:
让你有计算height
基础上height
的中UILabel
的加空间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;
}
这headerString
是您要填充的任何字符串UILabel
,并且281号是width
的UILabel
(在中设置Interface Builder
)