iOS 以编程方式为表格视图单元格内容创建 NSLayoutConstraint
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20317277/
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
iOS create NSLayoutConstraint programmatically for table view cell content
提问by Bartosz Bialecki
I want to add in the cellForRowAtIndexPath some views to my cell content view and for them constraints but nothing works. I have something like this:
我想在 cellForRowAtIndexPath 中添加一些视图到我的单元格内容视图和它们的约束,但没有任何效果。我有这样的事情:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:imageView
attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeLeft multiplier:1.0f constant:10.0f];
[cell.contentView addConstraint:constraint];
How should I do this?
我该怎么做?
回答by Rob
A couple of observations:
几个观察:
Your creation of this particular constraint is correct. Clearly, you can't just set the left constraint, but you need to specify all of the constraints that will unambiguously define the
frame
of the cell's subviews. For example, define not only the left (or leading) constraint, but also the top, bottom, and width constraints. Or define the left constraint plus the the width and height constraints, and specify the vertical y constraint. Lots of different ways to do it, but the key is that you have to add all of the constraints that will unambiguously define theframe
of all of the subviews.For example, you might have something like the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; UIImageView *customImageView; UILabel *customLabel; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; customImageView = [[UIImageView alloc] init]; customImageView.translatesAutoresizingMaskIntoConstraints = NO; customImageView.tag = IMAGEVIEWTAG; [cell.contentView addSubview:customImageView]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:25.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:30.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:3.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-3.0]]; customLabel = [[UILabel alloc] init]; customLabel.translatesAutoresizingMaskIntoConstraints = NO; customLabel.tag = LABELTAG; [cell.contentView addSubview:customLabel]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:customImageView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:10.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:-10.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:3.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-3.0]]; } else { customImageView = (id)[cell.contentView viewWithTag:IMAGEVIEWTAG]; customLabel = (id)[cell.contentView viewWithTag:LABELTAG]; } customImageView.image = ...; customLabel.text = ...; return cell; }
Clearly, you'd frequently use a
UITableViewCell
subclass to facilitate the process of keeping track of your custom controls, but I wanted to keep the example simple.If you're ever unsure of whether the constraints have been defined unambiguously, run the app and after the UI has been presented, pause the app and enter the following at the
(lldb)
prompt:po [[UIWindow keyWindow] _autolayoutTrace]
This will inform you if any of the views are ambiguously defined (i.e. whether there are any constraints missing).
If you want to see what the
frame
is for all of the views, you can enter the following at the(lldb)
prompt:po [[UIWindow keyWindow] recursiveDescription]
Make sure to specify
translatesAutoresizingMaskIntoConstraints
toNO
for all of the subviews, as I did in the above code sample.While you can define the constraints using
constraintWithItem
, frequently people will useconstraintsWithVisualFormat
, as you can often define constraints more concisely that way. Contrast the above code sample with this code sample:- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; UIImageView *customImageView; UILabel *customLabel; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; customImageView = [[UIImageView alloc] init]; customImageView.translatesAutoresizingMaskIntoConstraints = NO; customImageView.tag = IMAGEVIEWTAG; [cell.contentView addSubview:customImageView]; customLabel = [[UILabel alloc] init]; customLabel.translatesAutoresizingMaskIntoConstraints = NO; customLabel.tag = LABELTAG; [cell.contentView addSubview:customLabel]; NSDictionary *views = NSDictionaryOfVariableBindings(customImageView, customLabel); [cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-25-[customImageView(30)]-[customLabel]|" options:0 metrics:nil views:views]]; [cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-3-[customImageView]-3-|" options:0 metrics:nil views:views]]; [cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-3-[customLabel]-3-|" options:0 metrics:nil views:views]]; } else { customImageView = (id)[cell.contentView viewWithTag:IMAGEVIEWTAG]; customLabel = (id)[cell.contentView viewWithTag:LABELTAG]; } customImageView.image = ...; customLabel.text = ...; return cell; }
您创建的此特定约束是正确的。显然,您不能只设置左约束,而是需要指定将明确定义
frame
单元格子视图的所有约束。例如,不仅要定义左(或前导)约束,还要定义顶部、底部和宽度约束。或者定义左约束加上宽度和高度约束,并指定垂直 y 约束。有很多不同的方法可以做到这一点,但关键是您必须添加所有约束,这些约束将明确定义frame
所有子视图的 。例如,您可能有以下内容:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; UIImageView *customImageView; UILabel *customLabel; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; customImageView = [[UIImageView alloc] init]; customImageView.translatesAutoresizingMaskIntoConstraints = NO; customImageView.tag = IMAGEVIEWTAG; [cell.contentView addSubview:customImageView]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:25.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:30.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:3.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customImageView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-3.0]]; customLabel = [[UILabel alloc] init]; customLabel.translatesAutoresizingMaskIntoConstraints = NO; customLabel.tag = LABELTAG; [cell.contentView addSubview:customLabel]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:customImageView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:10.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:-10.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:3.0]]; [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:customLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:cell.contentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-3.0]]; } else { customImageView = (id)[cell.contentView viewWithTag:IMAGEVIEWTAG]; customLabel = (id)[cell.contentView viewWithTag:LABELTAG]; } customImageView.image = ...; customLabel.text = ...; return cell; }
显然,您经常使用
UITableViewCell
子类来促进跟踪自定义控件的过程,但我想让示例保持简单。如果您不确定是否明确定义了约束,请运行应用程序,并在显示 UI 后暂停应用程序并在
(lldb)
提示符处输入以下内容:po [[UIWindow keyWindow] _autolayoutTrace]
如果任何视图定义不明确(即是否缺少任何约束),这将通知您。
如果要查看
frame
所有视图的含义,可以在(lldb)
提示符下输入以下内容:po [[UIWindow keyWindow] recursiveDescription]
确保为所有子视图指定
translatesAutoresizingMaskIntoConstraints
toNO
,就像我在上面的代码示例中所做的那样。虽然您可以使用 定义约束
constraintWithItem
,但人们经常会使用constraintsWithVisualFormat
,因为您通常可以通过这种方式更简洁地定义约束。将上面的代码示例与此代码示例进行对比:- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; UIImageView *customImageView; UILabel *customLabel; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; customImageView = [[UIImageView alloc] init]; customImageView.translatesAutoresizingMaskIntoConstraints = NO; customImageView.tag = IMAGEVIEWTAG; [cell.contentView addSubview:customImageView]; customLabel = [[UILabel alloc] init]; customLabel.translatesAutoresizingMaskIntoConstraints = NO; customLabel.tag = LABELTAG; [cell.contentView addSubview:customLabel]; NSDictionary *views = NSDictionaryOfVariableBindings(customImageView, customLabel); [cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-25-[customImageView(30)]-[customLabel]|" options:0 metrics:nil views:views]]; [cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-3-[customImageView]-3-|" options:0 metrics:nil views:views]]; [cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-3-[customLabel]-3-|" options:0 metrics:nil views:views]]; } else { customImageView = (id)[cell.contentView viewWithTag:IMAGEVIEWTAG]; customLabel = (id)[cell.contentView viewWithTag:LABELTAG]; } customImageView.image = ...; customLabel.text = ...; return cell; }