自动布局让我的生活变得困难。从理论上讲,当我切换时它会非常有用,但我似乎总是在打它。
我做了一个试图找到帮助的演示项目。有没有人知道如何在调整视图大小时使视图之间的间距均匀增加或减少?
这是三个标签(手动间隔垂直):
我想要的是让他们在旋转时均匀地调整间距(不是视图大小)。默认情况下,顶视图和底视图朝中心方向挤压:
答案 0 :(得分:309)
看,没有间隔!
根据我原来答案评论部分的建议,特别是@Rivera的有用建议,我简化了原来的答案。
我正在使用GIF来说明这是多么简单。我希望你能找到有用的GIF。为了防止您遇到GIF问题,我在下面用简单的屏幕截图包含了旧答案。
<强>说明:强>
1)添加按钮或标签。我正在使用3个按钮。
2)从每个按钮添加一个中心x约束到超级视图:
3)从每个按钮添加约束到底部布局约束:
4)按如下方式调整上面#3中添加的约束:
a)选择约束, b)删除常量(设为0), c)更改乘数如下:取按钮数+ 1,从顶部开始,将乘数设置为 buttonCountPlus1:1 ,然后 buttonCountPlus1 :2 ,最后是 buttonCountPlus1:3 。 (如果你有兴趣,我会在下面的旧答案中解释我从哪里得到这个公式。)
5)这是一个正在运行的演示!
注意:如果你的按钮有更高的高度,那么你需要在常数值中补偿这个,因为约束来自按钮的底部。
旧答案
尽管Apple的文档和Erica Sadun的优秀书籍( Auto Layout Demystified )说,但是可以在没有间隔符的情况下均匀地分隔视图。这在IB和代码中非常简单,可以平均分配任意数量的元素。您只需要一个称为“部分公式”的数学公式。它比解释更简单。我会尽力在IB中展示它,但在代码中也很容易。
在相关示例中,您将
1)首先将每个标签设置为具有中心约束。这很简单。只需控制从每个标签拖动到底部。
2)按住shift,因为您可能还要添加我们将要使用的其他约束,即“底部空间到底部布局指南”。
3)选择“底部空间到底部布局指南”,“水平居中放置容器”。对所有3个标签执行此操作。
基本上,如果我们采用我们希望确定其坐标的标签并将其除以标签总数加1,那么我们可以添加一个数字以获得动态位置。我正在简化公式,但你可以用它同时设置水平间距或垂直和水平。它超级强大!
以下是我们的乘数。
Label1 = 1/4 = .25,
Label2 = 2/4 = .5,
Label3 = 3/4 = .75
(编辑:@Rivera评论说,您可以直接在乘数字段中使用比率,并使用xCode进行数学运算!)
4)所以,让我们选择Label1并选择底部约束。像这样:
5)在Attributes Inspector中选择“Second Item”。
6)从下拉列表中选择“反向第一和第二项”。
7)将常量和wC hAny值清零。 (如果需要,可以在这里添加偏移量)。
8)这是关键部分:在乘数字段中添加第一个乘数0.25。
9)当你在它时,将顶部的“第一项”设置为“中心”,因为我们希望将它居中到标签的y中心。这就是所有应该看的样子。
10)对每个标签重复此过程并插入相关的乘数:标签2为0.5,标签3为0.75。这是所有紧凑型设备的最终产品!超级简单。我一直在寻找很多涉及大量代码和间隔器的解决方案。这是我在这个问题上看到的最好的解决方案。
更新:@kraftydevil补充说,底部布局指南仅出现在故事板中,而不是出现在xib中。在xib中使用'Bottom Space to Container'。好抓!
答案 1 :(得分:204)
因此,我的方法允许您在界面构建器中执行此操作。你所做的是创建间隔观点&#39;您已设置为同等匹配高度。然后为标签添加顶部和底部约束(请参见屏幕截图)。
更具体地说,我对Spacer View 1&#39; Spacer View 1&#39;使用低于1000的高度约束进行超视图,并使高度等于所有其他的间隔视图&#39;。 &#39; Spacer View 4&#39;对superview有一个底部空间约束。每个标签对其最近的间隔视图都有相应的顶部和底部约束条件。
注意:请确保您的标签上有额外的顶部/底部空间限制以进行超视图;只是那些空间视图&#39;。这将是令人满意的,因为顶部和底部约束在空间视图1&#39;和&#39; Spacer View 4&#39;分别
Duh 1:我复制了我的观点,只是将其置于横向模式,这样你就可以看到它的效果。
Duh 2:&#39; spacer views&#39;可能是透明的。
Duh 3:这种方法可以横向应用。
答案 2 :(得分:90)
非常快速的Interface Builder解决方案:
对于在超视图中均匀分布的任意数量的视图,只需为每个视图分别为水平布局“Align Center X to superview”约束,或为垂直布局选择“Align Center Y superview”,并将Multiplier设置为{ {1}}(注意:有些人对N:p
有更好的运气 - 见下文)
其中
p:N
和
N = total number of views
第一个位置是1,然后是一个空格,使下一个位置3,所以p成为一个系列[1,3,5,7,9,...]。适用于任意数量的观点。
所以,如果你有3个视图空间,它看起来像这样:
编辑注意:p = position of the view including spaces
或N:p
的选择取决于对齐约束的关系顺序。如果“First Item”是Superview.Center,您可以使用p:N
,而如果Superview.Center是“Second Item”,您可以使用p:N
。如果有疑问,只需尝试两个......: - )
答案 3 :(得分:63)
从iOS 9开始,Apple已经通过(期待已久的)UIStackView
轻松实现了这一目标。只需在Interface Builder中选择要包含的视图,然后选择编辑器 - &gt;嵌入 - &gt;堆栈视图。为堆栈视图设置适当的宽度/高度/边距约束,并确保将“分布”属性设置为“等间距”&#39;:
当然,如果您需要支持iOS 8或更低版本,您必须选择其他选项之一。
答案 4 :(得分:50)
我一直在乘坐过山车,热爱自动布局并讨厌它。爱它的关键似乎是接受以下内容:
那就是说,你所尝试的并不简单,并且很难在界面构建器中实现。在代码中执行起来非常简单。这段代码在viewDidLoad
中创建并定位三个标签,告诉您如何:
// Create three labels, turning off the default constraints applied to views created in code
UILabel *label1 = [UILabel new];
label1.translatesAutoresizingMaskIntoConstraints = NO;
label1.text = @"Label 1";
UILabel *label2 = [UILabel new];
label2.translatesAutoresizingMaskIntoConstraints = NO;
label2.text = @"Label 2";
UILabel *label3 = [UILabel new];
label3.translatesAutoresizingMaskIntoConstraints = NO;
label3.text = @"Label 3";
// Add them all to the view
[self.view addSubview:label1];
[self.view addSubview:label2];
[self.view addSubview:label3];
// Center them all horizontally
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
// Center the middle one vertically
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];
// Position the top one half way up
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:label2 attribute:NSLayoutAttributeCenterY multiplier:0.5 constant:0]];
// Position the bottom one half way down
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:label2 attribute:NSLayoutAttributeCenterY multiplier:1.5 constant:0]];
正如我所说,UIView
中的几个类别方法简化了这段代码,但为了清楚起见,我已经完成了很长的路要走。
对于那些感兴趣的人,类别为here,并且它有一种方法可以沿特定轴均匀地间隔视图数组。
答案 5 :(得分:20)
这些解决方案中的大部分取决于存在奇数项目,因此您可以将中间项目置于中心位置。如果您仍然希望均匀分配偶数项目,该怎么办?这是一个更通用的解决方案。此类别将沿垂直轴或水平轴均匀分布任意数量的项目。
在超级视图中垂直分布4个标签的示例用法:
[self.view addConstraints:
[NSLayoutConstraint constraintsForEvenDistributionOfItems:@[label1, label2, label3, label4]
relativeToCenterOfItem:self.view
vertically:YES]];
<强> NSLayoutConstraint + EvenDistribution.h 强>
@interface NSLayoutConstraint (EvenDistribution)
/**
* Returns constraints that will cause a set of views to be evenly distributed horizontally
* or vertically relative to the center of another item. This is used to maintain an even
* distribution of subviews even when the superview is resized.
*/
+ (NSArray *) constraintsForEvenDistributionOfItems:(NSArray *)views
relativeToCenterOfItem:(id)toView
vertically:(BOOL)vertically;
@end
<强> NSLayoutConstraint + EvenDistribution.m 强>
@implementation NSLayoutConstraint (EvenDistribution)
+(NSArray *)constraintsForEvenDistributionOfItems:(NSArray *)views
relativeToCenterOfItem:(id)toView vertically:(BOOL)vertically
{
NSMutableArray *constraints = [NSMutableArray new];
NSLayoutAttribute attr = vertically ? NSLayoutAttributeCenterY : NSLayoutAttributeCenterX;
for (NSUInteger i = 0; i < [views count]; i++) {
id view = views[i];
CGFloat multiplier = (2*i + 2) / (CGFloat)([views count] + 1);
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view
attribute:attr
relatedBy:NSLayoutRelationEqual
toItem:toView
attribute:attr
multiplier:multiplier
constant:0];
[constraints addObject:constraint];
}
return constraints;
}
@end
答案 6 :(得分:17)
正确和最简单的方法是使用堆栈视图。
答案 7 :(得分:17)
查看开源库PureLayout。它提供了一些用于分发视图的API方法,包括每个视图之间的间距固定的视图(视图大小根据需要变化),以及每个视图的大小固定的位置(视图之间的间距根据需要而变化)。请注意,所有这些都是在没有使用任何&#34; spacer视图&#34; 的情况下完成的。
// NSArray+PureLayout.h
// ...
/** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (variable) in the dimension along the axis and will have spacing (fixed) between them. */
- (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
alignedTo:(ALAttribute)alignment
withFixedSpacing:(CGFloat)spacing;
/** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (fixed) in the dimension along the axis and will have spacing (variable) between them. */
- (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
alignedTo:(ALAttribute)alignment
withFixedSize:(CGFloat)size;
// ...
由于它是所有开源的,如果您有兴趣了解如何在没有间隔视图的情况下实现这一目标,只需看一下实现。 (这取决于对约束使用constant
和 multiplier
。)
答案 8 :(得分:9)
我遇到了类似的问题并发现了这篇文章。但是,当前提供的答案都没有以您希望的方式解决问题。它们不会使间距相等,而是平均分配标签的中心。重要的是要明白这是不一样的。我已经构建了一个小图来说明这一点。
有3个视图,全部高20pt。使用任何建议的方法均匀分布视图的中心并为您提供图示的布局。请注意,视图的y中心间隔相等。但是,superview和顶视图之间的间距是15pt,而子视图之间的间距只有5pt。为了使视图间隔均匀,这些视图应均为10pt,即所有蓝色箭头应为10pt。
尽管如此,我还没有提出一个好的通用解决方案。目前我最好的想法是在子视图之间插入“间距视图”,并将间距视图的高度设置为相等。
答案 9 :(得分:8)
我能够在IB中完全解决这个问题:
因此,如果您有三个标签,请将每个约束的乘数设置为0.1666667,0.5,0.833333。
答案 10 :(得分:5)
我发现了一种完美而简单的方法。自动布局不允许您平均调整空间大小,但它允许您平均调整视图大小。只需在字段之间放置一些不可见的视图,并告诉自动布局以保持它们的大小相同。它完美无缺!
但有一点需要注意;当我缩小界面设计器的大小时,有时它会混淆并留下标签,如果大小改变了奇数,就会产生冲突。否则它完美无缺。
编辑:我发现冲突成了问题。因此,我采用了一个间距约束,将其删除并用两个约束替换它,大于或等于和小于或等于。两者的大小相同,优先级低于其他约束。结果是没有进一步的冲突。
答案 11 :(得分:3)
swift 3版
let _redView = UIView()
_redView.backgroundColor = UIColor.red
_redView.translatesAutoresizingMaskIntoConstraints = false
let _yellowView = UIView()
_yellowView.backgroundColor = UIColor.yellow
_yellowView.translatesAutoresizingMaskIntoConstraints = false
let _blueView = UIView()
_blueView.backgroundColor = UIColor.blue
_blueView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(_redView)
self.view.addSubview(_yellowView)
self.view.addSubview(_blueView)
var views = ["_redView": _redView, "_yellowView": _yellowView, "_blueView":_blueView]
var nslayoutConstraint_H = NSLayoutConstraint.constraints(withVisualFormat: "|->=0-[_redView(40)]->=0-[_yellowView(40)]->=0-[_blueView(40)]->=0-|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views)
self.view.addConstraints(nslayoutConstraint_H)
var nslayoutConstraint_V = NSLayoutConstraint.constraints(withVisualFormat: "V:[_redView(60)]", options: NSLayoutFormatOptions.init(rawValue: 0), metrics: nil, views: views)
self.view.addConstraints(nslayoutConstraint_V)
let constraint_red = NSLayoutConstraint.init(item: self.view, attribute: .centerY, relatedBy: .equal, toItem: _redView, attribute: .centerY, multiplier: 1, constant: 0)
self.view.addConstraint(constraint_red)
let constraint_yellow = NSLayoutConstraint.init(item: self.view, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .centerX, multiplier: 1, constant: 0)
self.view.addConstraint(constraint_yellow)
let constraint_yellow1 = NSLayoutConstraint.init(item: _redView, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .leading, multiplier: 0.5, constant: 0)
self.view.addConstraint(constraint_yellow1)
let constraint_yellow2 = NSLayoutConstraint.init(item: _blueView, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .leading, multiplier: 1.5, constant: 40)
self.view.addConstraint(constraint_yellow2)
答案 12 :(得分:3)
基于Ben Dolman的回答,这可以更均匀地分配视图(使用填充等):
+(NSArray *)constraintsForEvenDistributionOfItems:(NSArray *)views
relativeToCenterOfItem:(id)toView vertically:(BOOL)vertically
{
NSMutableArray *constraints = [NSMutableArray new];
NSLayoutAttribute attr = vertically ? NSLayoutAttributeCenterY : NSLayoutAttributeCenterX;
CGFloat min = 0.25;
CGFloat max = 1.75;
CGFloat d = (max-min) / ([views count] - 1);
for (NSUInteger i = 0; i < [views count]; i++) {
id view = views[i];
CGFloat multiplier = i * d + min;
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view
attribute:attr
relatedBy:NSLayoutRelationEqual
toItem:toView
attribute:attr
multiplier:multiplier
constant:0];
[constraints addObject:constraint];
}
return constraints;
}
答案 13 :(得分:3)
使用标签至少可以正常工作:
@"H:|-15-[first(==second)]-[second(==third)]-[third(==first)]-15-|
如果第一个宽度与第二个宽度相同,第二个宽度与第三个相同,则第三个宽度相同,那么它们都将获得相同的宽度......您可以水平(H)和垂直(V)进行。
答案 14 :(得分:3)
检查https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html是否有关于解决问题的详细说明。
答案 15 :(得分:2)
这是一个垂直居中任意数量子视图的解决方案,即使它们具有唯一的大小。你想要做的是制作一个中级容器,在superview中居中,然后将所有子视图放在容器中并相互安排。但至关重要的是,您还需要将它们限制在容器的顶部和底部,这样容器可以在superview中正确调整大小并居中。通过从子视图中确定正确的高度,容器可以垂直居中。
在此示例中,self
是您以所有子视图为中心的超级视图。
NSArray *subviews = @[ (your subviews in top-to-bottom order) ];
UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
container.translatesAutoresizingMaskIntoConstraints = NO;
for (UIView *subview in subviews) {
subview.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:subview];
}
[self addSubview:container];
[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeRight multiplier:1.0f constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f]];
if (0 < subviews.count) {
UIView *firstSubview = subviews[0];
[container addConstraint:[NSLayoutConstraint constraintWithItem:firstSubview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
toItem:container attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f]];
UIView *lastSubview = subviews.lastObject;
[container addConstraint:[NSLayoutConstraint constraintWithItem:lastSubview attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
toItem:container attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f]];
UIView *subviewAbove = nil;
for (UIView *subview in subviews) {
[container addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
toItem:container attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f]];
if (subviewAbove) {
[container addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
toItem:subviewAbove attribute:NSLayoutAttributeBottom multiplier:1.0f constant:10.0f]];
}
subviewAbove = subview;
}
}
答案 16 :(得分:2)
我知道自第一个答案以来已经有一段时间了,但我遇到了同样的问题,我想分享我的解决方案。未来几代......
我在viewDidLoad上设置我的观点:
- (void)viewDidLoad {
[super viewDidLoad];
cancelButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
[cancelButton setTitle:@"Cancel" forState:UIControlStateNormal];
[self.view addSubview:cancelButton];
middleButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
middleButton.translatesAutoresizingMaskIntoConstraints = NO;
[middleButton setTitle:@"Middle" forState:UIControlStateNormal];
[self.view addSubview:middleButton];
nextButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
nextButton.translatesAutoresizingMaskIntoConstraints = NO;
[nextButton setTitle:@"Next" forState:UIControlStateNormal];
[self.view addSubview:nextButton];
[self.view setNeedsUpdateConstraints];
}
然后,在updateViewConstrains上,首先删除所有约束,然后创建视图字典,然后计算视图之间使用的空间。之后,我只使用可视语言格式来设置约束:
- (void)updateViewConstraints {
[super updateViewConstraints];
[self.view removeConstraints:self.view.constraints];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(cancelButton, nextButton, middleButton);
float distance=(self.view.bounds.size.width-cancelButton.intrinsicContentSize.width-nextButton.intrinsicContentSize.width-middleButton.intrinsicContentSize.width-20-20)/ ([viewsDictionary count]-1); // 2 times 20 counts for the left & rigth margins
NSNumber *distancies=[NSNumber numberWithFloat:distance];
// NSLog(@"Distancies: %@", distancies);
//
// NSLog(@"View Width: %f", self.view.bounds.size.width);
// NSLog(@"Cancel Width: %f", cancelButton.intrinsicContentSize.width);
// NSLog(@"Middle Width: %f", middleButton.intrinsicContentSize.width);
// NSLog(@"Next Width: %f", nextButton.intrinsicContentSize.width);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-[cancelButton]-dis-[middleButton]-dis-[nextButton]-|"
options:NSLayoutFormatAlignAllBaseline
metrics:@{@"dis":distancies}
views:viewsDictionary];
[self.view addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[nextButton]-|"
options:0
metrics:nil
views:viewsDictionary];
[self.view addConstraints:constraints];
}
这种方法的好处是你必须做很少的数学。我并不是说这是一个完美的解决方案,但我的工作是为了实现我想要实现的布局。
我希望它有所帮助。
答案 17 :(得分:2)
这是另一个答案。我正在回答类似的问题,看到链接引用了这个问题。我没有看到任何类似于我的答案。所以,我想在这里写一下。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.whiteColor()
setupViews()
}
var constraints: [NSLayoutConstraint] = []
func setupViews() {
let container1 = createButtonContainer(withButtonTitle: "Button 1")
let container2 = createButtonContainer(withButtonTitle: "Button 2")
let container3 = createButtonContainer(withButtonTitle: "Button 3")
let container4 = createButtonContainer(withButtonTitle: "Button 4")
view.addSubview(container1)
view.addSubview(container2)
view.addSubview(container3)
view.addSubview(container4)
[
// left right alignment
container1.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: 20),
container1.rightAnchor.constraintEqualToAnchor(view.rightAnchor, constant: -20),
container2.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
container2.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),
container3.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
container3.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),
container4.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
container4.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),
// place containers one after another vertically
container1.topAnchor.constraintEqualToAnchor(view.topAnchor),
container2.topAnchor.constraintEqualToAnchor(container1.bottomAnchor),
container3.topAnchor.constraintEqualToAnchor(container2.bottomAnchor),
container4.topAnchor.constraintEqualToAnchor(container3.bottomAnchor),
container4.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor),
// container height constraints
container2.heightAnchor.constraintEqualToAnchor(container1.heightAnchor),
container3.heightAnchor.constraintEqualToAnchor(container1.heightAnchor),
container4.heightAnchor.constraintEqualToAnchor(container1.heightAnchor)
]
.forEach { $0.active = true }
}
func createButtonContainer(withButtonTitle title: String) -> UIView {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
let button = UIButton(type: .System)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle(title, forState: .Normal)
view.addSubview(button)
[button.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor),
button.leftAnchor.constraintEqualToAnchor(view.leftAnchor),
button.rightAnchor.constraintEqualToAnchor(view.rightAnchor)].forEach { $0.active = true }
return view
}
}
同样,这也可以通过iOS9 UIStackViews轻松完成。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.greenColor()
setupViews()
}
var constraints: [NSLayoutConstraint] = []
func setupViews() {
let container1 = createButtonContainer(withButtonTitle: "Button 1")
let container2 = createButtonContainer(withButtonTitle: "Button 2")
let container3 = createButtonContainer(withButtonTitle: "Button 3")
let container4 = createButtonContainer(withButtonTitle: "Button 4")
let stackView = UIStackView(arrangedSubviews: [container1, container2, container3, container4])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .Vertical
stackView.distribution = .FillEqually
view.addSubview(stackView)
[stackView.topAnchor.constraintEqualToAnchor(view.topAnchor),
stackView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor),
stackView.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: 20),
stackView.rightAnchor.constraintEqualToAnchor(view.rightAnchor, constant: -20)].forEach { $0.active = true }
}
func createButtonContainer(withButtonTitle title: String) -> UIView {
let button = UIButton(type: .Custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.redColor()
button.setTitleColor(UIColor.whiteColor(), forState: .Normal)
button.setTitle(title, forState: .Normal)
let buttonContainer = UIStackView(arrangedSubviews: [button])
buttonContainer.distribution = .EqualCentering
buttonContainer.alignment = .Center
buttonContainer.translatesAutoresizingMaskIntoConstraints = false
return buttonContainer
}
}
请注意,它与上述方法完全相同。它添加了四个容器视图,它们被均等地填充,并且视图被添加到每个堆叠视图中,该视图在中心对齐。但是,这个版本的UIStackView减少了一些代码并且看起来不错。
答案 18 :(得分:2)
我刚刚使用乘数功能解决了我的问题。我不确定它适用于所有情况,但对我来说它完美无缺。我是Xcode 6.3 FYI。
我最终做的是:
1)首先让我的按钮位于320px宽度的屏幕上,按照我想要的方式在320px设备上进行分配。
2)然后我在所有按钮上为superview添加了一个前导空间约束。
3)然后我修改了前导空格的属性,使常量为0,乘数为x偏移除以屏幕宽度(例如我的第一个按钮距离左边8px,所以我将乘数设置为8 / 320)
4)然后,重要的一步是将约束关系中的第二个项目更改为superview.Trailing而不是superview.leading。这是关键因为superview.Leading是0并且在我的情况下尾随是320,所以8/320在320px设备上是8 px,然后当superview的宽度变为640或者其他什么时,所有视图都移动到比例相对于320px屏幕尺寸的宽度。这里的数学更容易理解。
答案 19 :(得分:2)
许多答案都不正确,但要多得多。在这里,我只是以编程方式编写解决方案,三个视图是水平对齐,不使用间隔视图,但仅当在故事板中使用时标识的宽度已知时才有效。
NSDictionary *views = NSDictionaryOfVariableBindings(_redView, _yellowView, _blueView);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|->=0-[_redView(40)]->=0-[_yellowView(40)]->=0-[_blueView(40)]->=0-|" options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_redView(60)]" options:0 metrics:nil views:views]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_redView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_redView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeLeading multiplier:0.5 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_blueView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeLeading multiplier:1.5 constant:40]];
答案 20 :(得分:1)
晚会,但我有一个工作的解决方案,水平创建一个间距菜单。可以使用NSLayoutConstraint中的==
const float MENU_HEIGHT = 40;
- (UIView*) createMenuWithLabels: (NSArray *) labels
// labels is NSArray of NSString
UIView * backgroundView = [[UIView alloc]init];
backgroundView.translatesAutoresizingMaskIntoConstraints = false;
NSMutableDictionary * views = [[NSMutableDictionary alloc] init];
NSMutableString * format = [[NSMutableString alloc] initWithString: @"H:|"];
NSString * firstLabelKey;
for(NSString * str in labels)
{
UILabel * label = [[UILabel alloc] init];
label.translatesAutoresizingMaskIntoConstraints = false;
label.text = str;
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor whiteColor];
[backgroundView addSubview: label];
[label fixHeightToTopBounds: MENU_HEIGHT-2];
[backgroundView addConstraints: [label fixHeightToTopBounds: MENU_HEIGHT]];
NSString * key = [self camelCaseFromString: str];
[views setObject: label forKey: key];
if(firstLabelKey == nil)
{
[format appendString: [NSString stringWithFormat: @"[%@]", key]];
firstLabelKey = key;
}
else
{
[format appendString: [NSString stringWithFormat: @"[%@(==%@)]", key, firstLabelKey]];
}
}
[format appendString: @"|"];
NSArray * constraints = [NSLayoutConstraint constraintsWithVisualFormat: (NSString *) format
options: 0
metrics: nil
views: (NSDictionary *) views];
[backgroundView addConstraints: constraints];
return backgroundView;
}
答案 21 :(得分:1)
另一种方法可能是使顶部和底部标签分别具有相对于视图顶部和底部的约束,并且中间视图分别具有相对于第一和第三视图的顶部和底部约束。
请注意,通过拖动彼此接近的视图,您可以更多地控制约束,直到出现引导虚线 - 这些表示将形成的两个对象之间的约束,而不是在对象和超级视图之间。
在这种情况下,您可能希望将约束更改为“大于或等于”所需的值,而不是“等于”以允许它们调整大小。不确定这是否能完全符合您的要求。
答案 22 :(得分:1)
我为第一个项目设置宽度值(&gt; =宽度)和每个项目之间的最小距离(&gt; =距离)。然后我使用Ctrl在第一个上拖动第二个,第三个...项目来链接项目之间的依赖关系。
答案 23 :(得分:1)
是的,您可以仅在界面构建器中执行此操作而无需编写代码 - 需要注意的是,您要调整标签大小而不是分配空格。在这种情况下,将Label 2的X和Y与superview对齐,使其固定在中心位置。然后将标签1的垂直空间设置为超视图,并将2标记为标准,重复标记3.设置标签2后,设置标签1和3的最简单方法是调整它们直到它们对齐为止。
这是水平显示,请注意标签1和2之间的垂直空间设置为标准:
这是肖像版:
我发现由于标签之间的标准空间与superview的标准空间之间的差异,它们在基线之间并非绝对100%相等。如果这困扰你,请将大小设置为0而不是标准
答案 24 :(得分:1)
我做了一个可能有帮助的功能。 这个用法示例:
[self.view addConstraints: [NSLayoutConstraint fluidConstraintWithItems:NSDictionaryOfVariableBindings(button1, button2, button3)
asString:@[@"button1", @"button2", @"button3"]
alignAxis:@"V"
verticalMargin:100
horizontalMargin:50
innerMargin:25]];
会导致vertical distribution(抱歉没有嵌入图片的10个声誉)。如果您更改轴和一些边距值:
alignAxis:@"H"
verticalMargin:120
horizontalMargin:20
innerMargin:10
我是iOS中的新手但是瞧!
<强> EvenDistribution.h 强>
@interface NSLayoutConstraint (EvenDistribution)
/**
* Returns constraints that will cause a set of subviews
* to be evenly distributed along an axis.
*/
+ (NSArray *) fluidConstraintWithItems:(NSDictionary *) views
asString:(NSArray *) stringViews
alignAxis:(NSString *) axis
verticalMargin:(NSUInteger) vMargin
horizontalMargin:(NSUInteger) hMargin
innerMargin:(NSUInteger) inner;
@end
<强> EvenDistribution.m 强>
#import "EvenDistribution.h"
@implementation NSLayoutConstraint (EvenDistribution)
+ (NSArray *) fluidConstraintWithItems:(NSDictionary *) dictViews
asString:(NSArray *) stringViews
alignAxis:(NSString *) axis
verticalMargin:(NSUInteger) vMargin
horizontalMargin:(NSUInteger) hMargin
innerMargin:(NSUInteger) iMargin
{
NSMutableArray *constraints = [NSMutableArray arrayWithCapacity: dictViews.count];
NSMutableString *globalFormat = [NSMutableString stringWithFormat:@"%@:|-%d-",
axis,
[axis isEqualToString:@"V"] ? vMargin : hMargin
];
for (NSUInteger i = 0; i < dictViews.count; i++) {
if (i == 0)
[globalFormat appendString:[NSString stringWithFormat: @"[%@]-%d-", stringViews[i], iMargin]];
else if(i == dictViews.count - 1)
[globalFormat appendString:[NSString stringWithFormat: @"[%@(==%@)]-", stringViews[i], stringViews[i-1]]];
else
[globalFormat appendString:[NSString stringWithFormat: @"[%@(==%@)]-%d-", stringViews[i], stringViews[i-1], iMargin]];
NSString *localFormat = [NSString stringWithFormat: @"%@:|-%d-[%@]-%d-|",
[axis isEqualToString:@"V"] ? @"H" : @"V",
[axis isEqualToString:@"V"] ? hMargin : vMargin,
stringViews[i],
[axis isEqualToString:@"V"] ? hMargin : vMargin];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:localFormat
options:0
metrics:nil
views:dictViews]];
}
[globalFormat appendString:[NSString stringWithFormat:@"%d-|",
[axis isEqualToString:@"V"] ? vMargin : hMargin
]];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:globalFormat
options:0
metrics:nil
views:dictViews]];
return constraints;
}
@end
答案 25 :(得分:1)
在InterfaceBuilder中解决此问题非常简单:
将居中标签(label2)设置为“容器中的水平中心”和“容器中的垂直中心”
选择居中标签和顶部标签(label1 + label2)并为Vertical Spacing添加 TWO 约束。 一个 大于或等于最小间距。 一个 小于或等于最大间距。
中心标签和底部标签(label2 + label3)相同。
此外,您还可以向label1添加两个约束 - Top Space To SuperView和两个约束到label2 - Bottom Space To SuperView。
结果将是所有4个间距将平均改变它们的大小。
答案 26 :(得分:0)
Android有一种在我想模仿的基于约束的布局系统中将视图链接在一起的方法。搜索将我带到这里,但没有一个答案奏效。我不想使用StackViews,因为它们往往使我更加悲痛,而不是预先保存。我最终创建了一个使用位于视图之间的UILayoutGuides的解决方案。通过控制宽度,可以使用Android的说法来分配不同类型的分布和链样式。该函数接受前导和尾随锚,而不是父视图。这允许将链放置在两个任意视图之间,而不是分布在父视图内部。它确实使用了UILayoutGuide
,仅在iOS 9+中可用,但这不再是问题。
public enum LayoutConstraintChainStyle {
case spread //Evenly distribute between the anchors
case spreadInside //Pin the first & last views to the sides and then evenly distribute
case packed //The views have a set space but are centered between the anchors.
}
public extension NSLayoutConstraint {
static func chainHorizontally(views: [UIView],
leadingAnchor: NSLayoutXAxisAnchor,
trailingAnchor: NSLayoutXAxisAnchor,
spacing: CGFloat = 0.0,
style: LayoutConstraintChainStyle = .spread) -> [NSLayoutConstraint] {
var constraints = [NSLayoutConstraint]()
guard views.count > 1 else { return constraints }
guard let first = views.first, let last = views.last, let superview = first.superview else { return constraints }
//Setup the chain of views
var distributionGuides = [UILayoutGuide]()
var previous = first
let firstGuide = UILayoutGuide()
superview.addLayoutGuide(firstGuide)
distributionGuides.append(firstGuide)
firstGuide.identifier = "ChainDistribution\(distributionGuides.count)"
constraints.append(firstGuide.leadingAnchor.constraint(equalTo: leadingAnchor))
constraints.append(first.leadingAnchor.constraint(equalTo: firstGuide.trailingAnchor, constant: spacing))
views.dropFirst().forEach { view in
let g = UILayoutGuide()
superview.addLayoutGuide(g)
distributionGuides.append(g)
g.identifier = "ChainDistribution\(distributionGuides.count)"
constraints.append(contentsOf: [
g.leadingAnchor.constraint(equalTo: previous.trailingAnchor),
view.leadingAnchor.constraint(equalTo: g.trailingAnchor)
])
previous = view
}
let lastGuide = UILayoutGuide()
superview.addLayoutGuide(lastGuide)
constraints.append(contentsOf: [lastGuide.leadingAnchor.constraint(equalTo: last.trailingAnchor),
lastGuide.trailingAnchor.constraint(equalTo: trailingAnchor)])
distributionGuides.append(lastGuide)
//Space the according to the style.
switch style {
case .packed:
if let first = distributionGuides.first, let last = distributionGuides.last {
constraints.append(first.widthAnchor.constraint(greaterThanOrEqualToConstant: spacing))
constraints.append(last.widthAnchor.constraint(greaterThanOrEqualToConstant: spacing))
constraints.append(last.widthAnchor.constraint(equalTo: first.widthAnchor))
constraints.append(contentsOf:
distributionGuides.dropFirst().dropLast()
.map { $0.widthAnchor.constraint(equalToConstant: spacing) }
)
}
case .spread:
if let first = distributionGuides.first {
constraints.append(contentsOf:
distributionGuides.dropFirst().map { $0.widthAnchor.constraint(equalTo: first.widthAnchor) })
}
case .spreadInside:
if let first = distributionGuides.first, let last = distributionGuides.last {
constraints.append(first.widthAnchor.constraint(equalToConstant: spacing))
constraints.append(last.widthAnchor.constraint(equalToConstant: spacing))
let innerGuides = distributionGuides.dropFirst().dropLast()
if let key = innerGuides.first {
constraints.append(contentsOf:
innerGuides.dropFirst().map { $0.widthAnchor.constraint(equalTo: key.widthAnchor) }
)
}
}
}
return constraints
}
答案 27 :(得分:0)
我想水平对齐5张图像,所以最终我跟随了Mete的response,差别很小。
第一个图像将在等于0且乘数为1:5的容器中水平居中:
其余的图像都一样。例如,第五张(也是最后一张)图像将在等于0且乘数为9:5的容器中水平居中:
如Mete所解释的,顺序为1、3、5、7、9等。位置遵循相同的逻辑:第一个位置为1,然后为空格,第二个位置为3,依此类推。>
答案 28 :(得分:-1)
为什么不创建tableView并制作isScrollEnabled = false