如何垂直自动布局 UIViews 列表,一个在彼此之上?
Posted
技术标签:
【中文标题】如何垂直自动布局 UIViews 列表,一个在彼此之上?【英文标题】:How do I auto-layout a list of UIViews vertically, one on top of each other? 【发布时间】:2016-05-30 04:59:50 【问题描述】:let N = 4
let holder = UIView(frame: CGRectMake(0, 0, someWidth, N * 100))
for var i = 0; i <= N; i++
let content = UIView(frame: CGRect.zero) //height should be 100
content.backgroundColor = randomColor()
content.translatesAutoresizingMaskIntoConstraints = true
holder.addSubview(content)
上面的代码会将N个UIViews添加到holder
。我想以编程方式使用NSLayoutConstraints
垂直显示内容,一个在一个之上。
每个内容的高度应为 100。每个内容应在所有 4 条边上都有约束。第一个内容应该对holder
有顶部/前导/尾随约束(常量=0)。最后一个内容应该有底部/前导/尾随约束(常量=0)到holder
。
其余内容应该对其他同级内容具有顶部/底部约束 (constant=8)。
如何创建这些约束?
【问题讨论】:
有什么理由你更喜欢手动布局而不是NSStackView
?
【参考方案1】:
我为水平页面所做的相同。所以这里是代码
var scrollView = UIScrollView()
var contectView = UIView();
添加holderview的子视图
self.scrollView.translatesAutoresizingMaskIntoConstraints = false;
self.view.addSubview(self.scrollView);
self.contectView.translatesAutoresizingMaskIntoConstraints = false;
self.scrollView.addSubview(self.contectView);
为 holderView 创建约束
let views = ["contectView" : self.contectView , "scrollview" : self.scrollView ];
// create constraint for scrollview
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[scrollview]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[scrollview]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
// create constraint for contectview with low priority for width and height
self.scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[contectView(==scrollview@250)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
self.scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[contectView(==scrollview@250)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
制作水平字符串约束
var verticalConstraintsFormat = ""; // create horizantal constraint string
let subViews = NSMutableDictionary();
subViews["scrollView"] = self.scrollView;
创建动态页面
let totalPage : NSInteger = 10;
for i in 0...totalPage
// Create your page here
let objProductView = UIView()
objProductView.translatesAutoresizingMaskIntoConstraints = false;
objProductView.backgroundColor = UIColor(white: CGFloat(arc4random()%100)/CGFloat(100), alpha: 1)
self.contectView.addSubview(objProductView);
let key = NSString(format: "productView%d", i) as NSString
subViews[key] = objProductView;
if(i==0)
verticalConstraintsFormat = verticalConstraintsFormat.stringByAppendingFormat("V:|-0-[%@(==100)]", key);
else if (i==totalPage)
verticalConstraintsFormat = verticalConstraintsFormat.stringByAppendingFormat("-0-[%@(==100)]-0-|", key);
else
verticalConstraintsFormat = verticalConstraintsFormat.stringByAppendingFormat("-0-[%@(==100)]", key);
let verticalConstraintsFormat1 = NSString(format:"H:|[%@(==scrollView)]", key);
self.scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(verticalConstraintsFormat1 as String, options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: subViews as AnyObject as! [String : AnyObject]));
self.scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(verticalConstraintsFormat as String, options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: subViews as AnyObject as! [String : AnyObject]));
希望这会有所帮助。
【讨论】:
【参考方案2】:在我们的项目中,我们遇到了类似的问题,我们必须使用以编程方式添加的约束将大量视图添加到滚动视图的内容视图中。为每个视图编写约束不仅使视图控制器臃肿,而且是静态的。要添加另一个视图,我们必须再次编写约束。
我们最终创建了现在为我们管理这个的 UIView 的子类。我们将其命名为NNVerticalStackView.h,.m。
这个类的要点是下面的方法。
-(void)addView:(UIView *)view
belowView:(UIView *)viewAbove
aboveView:(UIView *)viewBelow
withHeight:(CGFloat)height
view.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:view];
if(height != NNStackViewContentHeightMetric && height > 0)
NSArray * heightConstraint = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[view(height)]" options:0 metrics:@@"height":@(height) views:NSDictionaryOfVariableBindings(view)];
[view addConstraint:heightConstraint.firstObject];
NSArray * defaultConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[view]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)];
NSLayoutConstraint * upperConstraint,* lowerConstraint;
if(viewAbove==self)
[self removeConstraint:_topConstraint];
upperConstraint = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[view]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)].firstObject;
_topConstraint = upperConstraint;
else
upperConstraint = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[viewAbove]-0-[view]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view,viewAbove)].firstObject;
if(viewBelow==self)
[self removeConstraint:_bottomConstraint];
lowerConstraint = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[view]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)].firstObject;
_bottomConstraint = lowerConstraint;
else
lowerConstraint = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[view]-0-[viewBelow]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view,viewBelow)].firstObject;
[self addConstraints:defaultConstraints];
[self addConstraints:@[upperConstraint,lowerConstraint]];
对于最顶层的视图,viewAbove
是容器视图(即self
),否则它代表正在添加的视图上方的 at 视图。
对于最底部的视图,viewBelow
是容器视图,否则它是正在添加的当前视图下方的视图。
所以有了这个子类,你打算做的事情可以很容易地编码。
let N = 4
let holder = NNVerticalStackView(frame: CGRectMake(0, 0, someWidth, CGFloat(N) * 100.0))
for _ in 0...N
let content = UIView(frame: CGRect.zero) //height should be 100
holder.insertStackItem(content, atIndex: 0, withItemHeight: 100.0)
对于偏移量,您可能需要更改自定义类来定义自己的偏移量。
【讨论】:
【参考方案3】:你可以这样做,
let content = UIView()
content.backgroundColor = UIColor.redColor()
content.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(content)
let topConstraint = NSLayoutConstraint(item: content, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)
view.addConstraint(topConstraint)
let leadingConstraint = NSLayoutConstraint(item: content, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0)
view.addConstraint(leadingConstraint)
let bottomConstraint = NSLayoutConstraint(item: content, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0)
view.addConstraint(bottomConstraint)
let trailingConstraint = NSLayoutConstraint(item: content, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 0)
view.addConstraint(trailingConstraint)
let heightConstraint = NSLayoutConstraint(item: content, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 100)
view.addConstraint(heightConstraint)
这里你应该考虑holder
而不是view
。
此约束适用于中间视图。对于第一个和最后一个视图场景应该是不同的,例如,
//Top constraint for first view should be like,
let topConstraintForfirstView = NSLayoutConstraint(item: content, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0)
view.addConstraint(topConstraintForfirstView)
//Bottom constraint for last view should be like,
let bottomConstraintForLastview = NSLayoutConstraint(item: content, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)
view.addConstraint(bottomConstraintForLastview)
您可以通过更改约束的constant
来管理空间或距离。如果您希望视图之间的垂直间距为 20 像素,那么您的常数为 top constraints should be 20
。
您可以参考this answer作为参考。
不要同时给出高度和底部。如果你给定高度,那么你不需要底部,如果你给底部,那么你不需要固定高度。
希望这会有所帮助:)
【讨论】:
以上是关于如何垂直自动布局 UIViews 列表,一个在彼此之上?的主要内容,如果未能解决你的问题,请参考以下文章