NSLayoutConstraint 用于代码中未知数量的子视图
Posted
技术标签:
【中文标题】NSLayoutConstraint 用于代码中未知数量的子视图【英文标题】:NSLayoutConstraint for unknown number of subviews in code 【发布时间】:2016-07-15 14:38:41 【问题描述】:我正在进入 ios 编程领域,并且或多或少地掌握了具有固定数量项目的自动布局。比如说,我们有一个 UILabel 代表标题,UILabel 代表副标题,那么约束的视觉格式是'V:|-[title]-10-[subtitle]-|'
但是如果我根据一些 API 响应动态创建子视图会怎样。例如,我需要添加 40 个子视图。我用视觉格式指定每个子视图并跟踪它们已经不现实了。正确的方法是什么?
我想,在我添加每个子视图之后,我会根据前一个视图使用constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
设置约束。这是要走的路,还是有更好的方法?
【问题讨论】:
【参考方案1】:我用视觉格式指定每个子视图并跟踪它们已经不现实了
为什么不呢?您似乎认为这是某种“非此即彼”的情况。没有什么能阻止您使用可视化格式来构建约束集合。没有法律规定您必须将所有您的约束放入一个视觉格式。
而且布局引擎不知道也不关心你用什么表示法来形成你的约束。约束就是约束!
考虑一下我在其中为滚动视图和其中的 30 个标签构建约束的代码:
var con = [NSLayoutConstraint]()
con.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[sv]|",
options:[], metrics:nil,
views:["sv":sv]))
con.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[sv]|",
options:[], metrics:nil,
views:["sv":sv]))
var previousLab : UILabel? = nil
for i in 0 ..< 30
let lab = UILabel()
// lab.backgroundColor = UIColor.redColor()
lab.translatesAutoresizingMaskIntoConstraints = false
lab.text = "This is label \(i+1)"
sv.addSubview(lab)
con.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-(10)-[lab]",
options:[], metrics:nil,
views:["lab":lab]))
if previousLab == nil // first one, pin to top
con.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|-(10)-[lab]",
options:[], metrics:nil,
views:["lab":lab]))
else // all others, pin to previous
con.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:[prev]-(10)-[lab]",
options:[], metrics:nil,
views:["lab":lab, "prev":previousLab!]))
previousLab = lab
con.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:[lab]-(10)-|",
options:[], metrics:nil,
views:["lab":previousLab!]))
NSLayoutConstraint.activateConstraints(con)
我正在使用可视化格式,但我在添加视图时一次执行一个约束(这正是您要问的)。
【讨论】:
哈。现在我觉得有点愚蠢:) 谢谢 请注意,如果您希望滚动视图将其contentSize.height
设置为实际允许滚动,您还需要将最后一个标签限制在滚动视图的底部。
@robmayoff 做到了,只是在粘贴时被切断了【参考方案2】:
答案真的取决于你想要什么样的布局,因为有更高级的工具可以制作一些布局。
从 iOS 9 开始,您可以使用UIStackView
来管理一行或一列视图。 By nesting stack views, you can easily make a grid of views. 要了解UIStackView
,请先观看“Mysteries of Auto Layout, Part 1” from WWDC 2015。如果您在代码中完成所有布局,则可以使用TZStackView
,这是UIStackView
的重新实现,适用于 iOS 7 及更高版本。
UICollectionView
是另一种用于在网格或许多其他排列中布置视图的工具。它比UIStackView
更复杂,但是有很多介绍性材料可以教你如何使用它,例如“Introducing Collection Views” from WWDC 2012。
【讨论】:
【参考方案3】:我构建了一个动态布局引擎(水平布局和垂直布局)。
它接受一组视图。
它将视图作为子视图添加到容器中。
然后动态组成一个垂直和水平的视觉布局字符串。 (它为使用 v%p 布局的每个视图生成一个唯一的“对象地址”字符串。 只需在 viewDidLoad 中调用:[myView addControls:@[view1, view2, view3]],它就会为您垂直布局。这是一个用于垂直布局的通用布局引擎。享受吧。
@implementation UIView (VerticalLayout)
- (void)addControls:(NSArray*)controls
align:(VerticalAlignment)alignment
withHeight:(CGFloat)height
verticalPadding:(CGFloat)verticalPadding
horizontalPadding:(CGFloat)horizontalPadding
topPadding:(CGFloat)topPadding
bottomPadding:(CGFloat)bottomPadding
withWidth:(CGFloat)width
NSMutableDictionary* bindings = [[NSMutableDictionary alloc] init];
NSDictionary *metrics = @
@"topPadding": @(topPadding),
@"bottomPadding": @(bottomPadding),
@"verticalPadding": @(verticalPadding),
@"horizontalPadding":@(horizontalPadding) ;
NSMutableString* verticalConstraint = [[NSMutableString alloc] initWithString:@"V:"];
if (alignment == VerticalAlignTop || alignment == VerticalAlignStretchToFullHeight)
[verticalConstraint appendString:@"|"];
for (UIView* view in controls)
BOOL isFirstView = [controls firstObject] == view;
BOOL isLastView = [controls lastObject] == view;
[self addSubview: view];
// Add to vertical constraint string
NSString* viewName = [NSString stringWithFormat:@"v%p",view];
[bindings setObject:view forKey:viewName];
NSString* yLeadingPaddingString = @"";
NSString* yTrailingPadding = @"";
if (isFirstView && topPadding > 0)
yLeadingPaddingString = @"-topPadding-";
else if (!isFirstView && verticalPadding > 0)
yLeadingPaddingString = @"-verticalPadding-";
if (isLastView && bottomPadding > 0)
yTrailingPadding = @"-bottomPadding-";
[verticalConstraint appendFormat:@"%@[%@%@]%@",
yLeadingPaddingString,
viewName,
height > 0 ? [NSString stringWithFormat:@"(%f)", height] : @"",
yTrailingPadding];
NSArray* constraints = [NSLayoutConstraint constraintsWithVisualFormat:
[NSString stringWithFormat: @"H:|-horizontalPadding-[%@%@]%@|",
viewName,
(width > 0 ? [NSString stringWithFormat:@"(%lf)", width] : @""),
width > 0 ? @"-" : @"-horizontalPadding-"]
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:metrics
views:bindings];
// high priority, but not required.
for (NSLayoutConstraint* constraint in constraints)
constraint.priority = 900;
// Add the horizontal constraint
[self addConstraints: constraints];
if (alignment == VerticalAlignBottom || alignment == VerticalAlignStretchToFullHeight)
[verticalConstraint appendString:@"|"];
NSArray* constraints = [NSLayoutConstraint
constraintsWithVisualFormat:verticalConstraint
options:NSLayoutFormatAlignAllLeading
metrics:metrics
views:bindings];
for (NSLayoutConstraint* constraint in constraints)
constraint.priority = 900;
// Add the vertical constraints for all these views.
[self addConstraints:constraints];
【讨论】:
以上是关于NSLayoutConstraint 用于代码中未知数量的子视图的主要内容,如果未能解决你的问题,请参考以下文章
获取 NSLayoutConstraint 标识符不适用于 topAnchor
@IBDesignable , NSLayoutConstraint 用于超级视图的比例乘数宽度?
NSLayoutConstraint 用于 UITableView 上的视图
在 iOS7 中设置 NSLayoutConstraint 常量