我做了一个演示项目来寻求帮助。有人知道如何在调整视图大小时使视图之间的间隔均匀地增加或减少吗?
这是三个标签(手动在垂直方向上均匀隔开):
我想要的是让他们在旋转时均匀调整其间距(而不是视图大小)。默认情况下,顶视图和底视图向中心倾斜:
#1 楼
因此,我的方法允许您在界面生成器中执行此操作。您要做的是创建“间隔视图”,将其设置为相等地匹配高度。然后将顶部和底部约束添加到标签(请参见屏幕截图)。更具体地说,我对“ Spacer View 1”有一个最高约束,以便以低于1000的优先级且高度等于所有其他“间隔视图”。 “ Spacer View 4”具有底部空间约束以进行超级视图。每个标签都有与其最接近的“间隔视图”相对应的顶部和底部约束。
注意:请确保您在标签上没有多余的顶部/底部空间约束以进行查看;只是“空间视图”中的那些。这是可以满足的,因为顶部和底部约束分别位于“ Space View 1”和“ Spacer View 4”上。
Duh 1:我复制了视图并仅将其置于横向模式,因此您可以
Duh 2:“间隔视图”可能是透明的。
Duh 3:这种方法可以水平应用。
评论
这有效。实际上,间隔视图可以被隐藏,它们仍将产生正确的布局。
–paulmelnikow
13年3月14日在2:47
这超级有帮助。就像只是将间隔视图标记为隐藏的注释一样。然后,您仍然可以在情节提要/ xib中看到它们,但是它们不会出现在您的应用程序中。非常好。
– Shulmey
13年7月7日在2:31
经过数小时的反复尝试,我几乎可以正常工作了。不幸的是,Xcode一直删除我的按钮到顶部和底部的空间约束,打破按钮和分隔符之间的链条,并用无用的顶部到视图的任意约束代替它们。
– Desty
13-10-24在11:19
在水平布局中,同样的方法适用于我-我有固定宽度的视图,并由等宽间隔视图分隔。不需要将其与任何代码混合在一起真是太好了。谢谢!
– Kamil Nomtek.com
14年2月15日在11:08
无需使用垫片。请参阅下面的答案。
–smileBot
2014年10月3日14:11
#2 楼
瞧,没有空格!基于原始答案的评论部分中的建议,尤其是@Rivera的有用建议,我简化了原始答案。
我正在使用gif来说明这是多么简单。我希望这些gif有用。万一您遇到gif的问题,我在下面以纯屏幕快照提供了旧答案。
说明:
1)添加您的按钮或标签。我正在使用3个按钮。
2)在每个按钮的超级视图中添加一个中心x约束:
3)添加一个每个按钮到底部布局约束的约束:
4)如下调整上述#3中添加的约束:
b)删除常数(设置为0),
c)如下更改乘数:取按钮数+ 1,从顶部开始,将乘数设置为buttonCountPlus1 :1,然后是buttonCountPlus1:2,最后是buttonCountPlus1:3。 (如果您有兴趣,请在下面的旧答案中说明从哪里获得此公式的。)
5)这是一个正在运行的演示!
注意:如果按钮的高度较大,则由于约束来自按钮的底部,因此需要用恒定值对此进行补偿。
旧答案
尽管Apple的文档和Erica Sadun的出色著作(《自动版图》已神秘化)说的话,仍可以在没有间隔符的情况下均匀地间隔视图。对于要均匀间隔的任意数量的元素,这在IB和代码中都很简单。您所需要的只是一个称为“部分公式”的数学公式。这比解释要容易。我将通过在IB中进行演示来尽力而为,但是在代码中这样做同样容易。
在所涉及的示例中,您会
1)首先将每个标签设置为具有中心约束。这很简单。只需控制从每个标签到底部的拖动。
2)按住shift键,因为您最好添加我们将要使用的其他约束,即“底部空间到底部布局的指南”。
3)选择“底部到底部的空间布局指南”和“在容器中水平居中”。对所有3个标签都执行此操作。
基本上,如果我们采用要确定其坐标的标签并将其除以标签总数加1,则我们可以将一个数字加到IB以获得动态位置。我正在简化公式,但是您可以将其用于设置水平间距或同时设置垂直和水平间距。超级强大!
这里是我们的乘法器。
Label1 = 1/4 = .25,
Label2 = 2/4 = .5 ,
Label3 = 3/4 = .75
(编辑:@Rivera评论说,您可以直接在乘数字段中直接使用比率,而xCode可以进行数学计算!)
4)因此,让我们选择Label1并选择底部约束。像这样:
5)在“属性”检查器中选择“第二个项目”。
6)从下拉列表中选择“反转第一和第二项”。
7)将常数和wC hAny值清零。 (如果需要,可以在此处添加偏移量)。
8)这是关键部分:在乘数字段中添加第一个乘数0.25。
9)当您在它上面时,将顶部的“第一项”设置为“ CenterY”,因为我们希望将其居中放置在标签的y中心。这是所有内容的外观。
10)对每个标签重复此过程,并插入相关的乘数:Label2为0.5,Label3为0.75。这是所有紧凑型设备所有方向的最终产品!超级简单。我一直在研究许多涉及大量代码和间隔符的解决方案。这是我在此问题上看到的最好的解决方案。
更新:@kraftydevil补充说,底部布局指南仅出现在情节提要中,而不出现在xibs中。在xibs中使用“容器的底部空间”。好赶上!
评论
我仍然在努力。我认为您必须使用Xcode 6,因为Xcode 5约束中没有'wC hAny'选项。当我按照您的指示进行操作时,所有子视图都停留在顶部中心位置。
– Kraftydevil
2014年9月23日下午3:37
@kraftydevil AFAIK底部布局指南仅出现在情节提要中,而不出现在xib中。在xibs中使用“容器的底部空间”。
–蒂莫尔·库奇卡洛夫(Timur Kuchkarov)
2014年10月15日下午5:25
噢–同样值得注意的是,Xcode 6中的IB支持比率乘数,因此您可以输入“ 1:4”,“ 2:5”,“ 3:4”等。
–本约翰
2014年12月5日在21:47
试图弄清楚如何水平执行此操作-像第一个TextView左起1/3,然后第二个TextView 2/3(剩下的路)... ...(编辑)得到它-执行前1/3(左一个)尾随空格到右边界,反向,零,像以前一样分配...
–kfmfe04
2014年12月31日18:49
对于水平间距,只需为每个子视图的超级视图设置尾随约束,然后设置比例乘数即可。 5:1、5:2、5:3等从左向右移动。
–user3344977
2015年12月30日,下午3:26
#3 楼
非常快速的Interface Builder解决方案:对于要在一个超级视图中均匀分布的任意数量的视图,只需为每个视图设置水平布局的“将X中心对齐到超级视图”约束,或为“将Y中心对齐到超级视图”约束垂直布局,并将乘数设置为
N:p
(注意:有些运气更好的p:N
-参见下文)其中
N = total number of views
和p = position of the view including spaces
第一个位置是1,然后是一个空格,使得下一个位置3,因此p成为级数[1,3,5,7,9,...]。适用于任意数量的视图。
因此,如果您有3个视图需要间隔,则它看起来像这样:
编辑注意:
N:p
或p:N
的选择取决于对齐约束的关系顺序。如果“第一项”是Superview.Center,则可以使用p:N
;如果“ Superview.Center”是“ Second Item”,则可以使用N:p
。如有疑问,请尝试一下... :-) 评论
如果您切换到其他语言,此解决方案将无法反映设计
– wod
15年6月17日在10:41
如果元素的大小不一样怎么办?这种模式不能正常工作,对吗?
–ucangetit
15年7月28日在20:48
我不知道此解决方案如何正确?屏幕边缘与外部元件之间的间隙是否不同于外部元件与中心元件之间的间隙?
–迈尔斯
2015年9月17日下午17:27
对于我来说,这不是“原样”,我不得不逆转乘数(例如,第一项的3:1转换为1:3)。如果有帮助,就把它扔出去。
– Ces
16年5月12日在3:01
感谢您的好答案,在iOS10上效果很好。是的,仅供参考,我有4个按钮。我使用的比例是:1 / 4、3 / 4、5 / 4、7 / 4。奇迹般有效!我很惊讶Apple如何使UI东西变得如此复杂,哈哈。
–RainCast
16-10-31在2:50
#4 楼
从iOS 9开始,Apple借助(期待已久的)UIStackView
使此操作非常容易。只需选择要包含在Interface Builder中的视图,然后选择Editor-> Embed In-> Stack视图。为堆栈视图设置适当的宽度/高度/边距约束,并确保将“分配”属性设置为“等间距”:当然,如果您需要支持iOS 8或更低版本,您必须选择其他选项之一。
评论
是的,很遗憾,它不具有复古兼容性,因此直到您的应用需要对IOS8 @Mete响应的支持才是目前最好的。
–ZiggyST
16-2-24在17:06
#5 楼
我一直在喜欢自动布局并讨厌它。喜欢它的关键似乎是要接受以下内容:接口构建器的编辑和约束的“有帮助的”自动创建约束,除了最琐碎的情况外,几乎对所有其他情况都没有用。创建类别以简化常见操作非常省力,因为代码是如此重复和冗长。
,这就是说,您尝试的操作并不简单,并且在接口构建器中很难实现。用代码做起来很简单。这段代码在
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
中,但为清楚起见,我在这里做了很长的路要走。 对于那些感兴趣的人来说,这里是类别,它有一种方法可以沿着特定的轴均匀分布视图阵列。
评论
先生,您是救生员。直到现在我还不了解#1。界面生成器使我发疯,但我认为它能够完成所有相同的事情。无论如何,我还是更喜欢使用代码,所以这是完美的,并且我会正确地使用类别方法。
–nothappybob
2012年10月25日19:46
尽管绝对接近,但这并不能完全解决所要求的确切问题(将视图大小保持相同,并且子视图之间的间距均匀)。该解决方案是精确的一般情况答案。
–smileyborg
13年8月19日在4:25
#6 楼
正确和最简单的方法是使用堆栈视图。将标签/视图添加到堆栈视图:
选择堆栈视图并将分布设置为相等间距:
将间距添加到最接近的邻居约束到堆栈视图并更新框架:
为所有标签添加高度限制(可选)。仅对于没有内在大小的视图才需要)。例如,标签在这里不需要高度限制,而只需要设置numberOfLines = 3或0。
享受预览:
评论
这仅是> = iOS 9.0,因此在开始实施之前请检查您的部署目标:)很高兴看到已添加其中!
–DivideByZer0
16年8月29日在21:55
现在2018,使用堆栈视图
–陈敬韶
18/09/11在19:35
#7 楼
这些解决方案中的大多数都取决于项目数量的奇数,以便您可以取中间的项目并居中。如果您仍然希望均匀分配一些物品,该怎么办?这是一个更通用的解决方案。此类别将沿垂直或水平轴均匀分布任意数量的项目。在其超级视图内垂直分布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
评论
它的工作方式是删除所有约束,然后立即将Width和Height约束添加到这些对象,然后调用方法ConstraintsForEvenDistributionOfItems:relativeToCenterOfItem:vertical:
– carlos_ms
13年4月2日,下午1:34
@carlos_ms我很想看看您的代码,因为当我尝试偶数个项目时,此代码在切换设备方向时非常有效。您确定不会无意间导致冲突的其他约束(例如自动调整大小的约束)吗?
–本·多尔曼
13年4月13日在18:09
这是创建4个标签并沿垂直轴均匀间隔的示例:gist.github.com/bdolman/5379465
–本·多尔曼
13年4月13日在18:17
#8 楼
查看开源库PureLayout。它提供了一些用于分发视图的API方法,其中包括每个视图之间的间距是固定的(视图大小根据需要而变化)以及每个视图的尺寸是固定的(视图之间的间距根据需要而变化)的变体。请注意,所有这些操作都是在不使用任何“分隔符视图”的情况下完成的。从NSArray + PureLayout.h:
// 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
作为约束。)评论
干得好!虽然您可能应该重命名该存储库,以便您可以通过cocoapods使用它。
– jrturton
13年8月19日在6:30
#9 楼
我遇到类似的问题,并发现了这篇文章。但是,当前提供的答案都无法以您想要的方式解决问题。它们不会平均分配间距,而是平均分配标签的中心。重要的是要了解这是不一样的。我构建了一个小图来说明这一点。有3个视图,都高20pt。使用任何建议的方法均等地分布视图的中心,并为您提供图示的布局。请注意,视图的y中心间距相等。但是,超级视图和顶视图之间的间隔为15pt,而子视图之间的间隔仅为5pt。为了使视图均等,它们都应为10pt,即所有蓝色箭头都应为10pt。不过,我还没有一个好的通用解决方案。目前,我最好的想法是在子视图之间插入“间距视图”,并将间距视图的高度设置为相等。
评论
我使用了隐藏的间隔视图的解决方案,效果很好。
–paulmelnikow
13年3月14日在2:42
#10 楼
我完全可以在IB中解决此问题:设置约束以将每个子视图的中心Y对齐到超级视图的底部边缘。
设置的乘数这些限制分别为1 / 2n,3 / 2n,5 / 2n,…,n-1 / 2n,其中n是您要分配的子视图数。
因此,如果您有三个标签,将每个约束的乘数设置为0.1666667、0.5、0.833333。
评论
我实际上不得不做1 / n 3 / n 5 / n 7 / n至(2n-1)/ n
–汉扎·马利克(Hamzah Malik)
16年4月3日在18:20
#11 楼
我找到了一种完美而简单的方法。自动布局不允许您平均调整空间大小,但可以让您平均调整视图大小。只需在您的字段之间放置一些不可见的视图,并告诉自动布局以使它们保持相同大小即可。不过要注意一点;当我在界面设计器中减小尺寸时,有时会感到困惑,并在原来的位置留下标签,如果将尺寸更改为奇数,则会发生冲突。否则,它会完美地工作。
编辑:我发现冲突成了一个问题。因此,我选择了一个间距约束,将其删除,并用两个约束(大于或等于和小于或等于)替换。两者的大小相同,并且优先级比其他限制要低得多。结果没有进一步的冲突。
#12 楼
在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 楼
检查https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html,其中包含有关解决问题的详细说明。评论
在将UIStackView作为执行此任务的最佳选择的iOS 9发布之后,Apple删除了此页面
–穆勒·塔卡
2015年12月16日下午4:52
#14 楼
使用标签至少可以正常工作:@"H:|-15-[first(==second)]-[second(==third)]-[third(==first)]-15-|
如果第一个与第二个具有相同的宽度,第二个与第三个具有相同的宽度,第三个与第一个具有相同的宽度,则它们将全部得到相同的宽度...您既可以水平(H),也可以垂直(V)。
#15 楼
迅捷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)
评论
详细说明此代码如何回答问题将对将来的访问者有所帮助。
–日航
16-10-25在23:35
#16 楼
我知道距第一个答案已经有一段时间了,但我遇到了同样的问题,我想分享自己的解决方案。为了世代相传...我在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 楼
这是一个解决方案,即使子视图具有唯一的大小,也可以将任何数量的子视图垂直居中。您要做的是制作一个中级容器,将其置于父视图的中心,然后将所有子视图放入容器中,并将它们彼此相对排列。但是至关重要的是,您还需要将它们限制在容器的顶部和底部,以便可以正确调整容器的大小并在超级视图中居中。通过从子视图中确定正确的高度,可以将容器垂直居中。在本示例中,self
是将所有子视图居中的超级视图。评论
当使用必须居中的不同大小的视图时,这是最佳解决方案。
– noobsmcgoobs
2015年9月25日4:16在
#18 楼
我只是使用乘法器功能解决了我的问题。我不确定它是否适用于所有情况,但对我而言,它工作得很好。我正在使用Xcode 6.3 FYI。我最终要做的是:
1)首先,将按钮放在320px宽的屏幕上,以我想要的方式分布在320px的设备上查看。
2)然后,我在所有按钮上添加了领先的Space约束以进行超级视图。
3)然后,我修改了前导空格的属性,使常量为0,乘数为x偏移除以屏幕的宽度(例如,我的第一个按钮距左边缘8像素,因此我将其设置为我的乘数为8/320)
4)然后,重要的步骤是将约束关系中的第二个项目更改为superview.Trailing而不是superview.leading。这是关键,因为superview.Leading为0且在我的情况下为320,所以在320px的设备上8/320为8 px,然后当Superview的宽度更改为640或任何其他值时,视图均以相对于宽度的比率移动320像素的屏幕尺寸。这里的数学更容易理解。
#19 楼
许多答案是不正确的,但是却有很多计数。在这里,我只是以编程方式编写一个解决方案,这三个视图是水平对齐的,没有使用间隔视图,但是只有在情节提要中使用标签的宽度时,该视图才有效。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 楼
这是另一个答案。我在回答类似的问题,并且看到指向该问题的链接。我没有看到任何类似于我的答案。所以,我想到了在这里编写它。再次,这也可以很容易地用iOS9 UIStackViews完成。
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 { 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 { q4312078q.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
}
}
.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 { q4312078q.active = true }
return view
}
}
请注意,它与上述方法完全相同。它添加了四个均等填充的容器视图,并且将一个视图添加到了每个居中对齐的堆栈视图。但是,此版本的UIStackView减少了一些代码,看起来不错。
#21 楼
另一种方法可能是使顶部和底部标签分别具有相对于视图顶部和底部的约束,而使中间视图具有分别相对于第一视图和第三视图的顶部和底部约束。请注意,您可以通过比紧靠彼此拖动视图直到出现虚线引导来更好地控制约束,这表示将要形成的两个对象之间的约束,而不是对象和父视图之间的约束。
在这种情况下,您将需要将约束更改为“大于或等于”所需值,而不是“等于”以允许它们调整大小。不知道这是否能完全满足您的要求。
#22 楼
在InterfaceBuilder中解决此问题的一种非常简单的方法:将居中标签(label2)设置为“容器中的水平中心”和“容器中的垂直中心”
选择居中的标签和顶部标签(label1 + label2),并为“垂直间距”添加两个约束。大于或等于最小间距的一个。小于或等于最大间距的一个。
居中的标签和底部的标签(label2 + label3)相同。
此外,您还可以向两个标签添加两个约束label1-SuperView的顶部空间和label2-SuperView的底部空间。
结果将是所有4个间距均等地改变其大小。
评论
我发现这仅在调整超级视图的大小时有效,以便间隔恰好采用最小值或最大值。当您将其大小调整为介于两者之间的值时,子视图不会平均分布。
–多里安·罗伊(Dorian Roy)
13年2月1日在11:24
#23 楼
我制作了一个可能会有用的函数。此用法示例:
[self.view addConstraints: [NSLayoutConstraint fluidConstraintWithItems:NSDictionaryOfVariableBindings(button1, button2, button3)
asString:@[@"button1", @"button2", @"button3"]
alignAxis:@"V"
verticalMargin:100
horizontalMargin:50
innerMargin:25]];
会导致垂直分布(对不起,没有十个声誉可嵌入图片)。而且,如果更改轴和一些边距值:
alignAxis:@"H"
verticalMargin:120
horizontalMargin:20
innerMargin:10
您将获得水平分布。
I' 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
#24 楼
是的,您可以仅在界面生成器中执行此操作,而无需编写代码-一个警告是,您正在调整标签的大小,而不是分配空白。在这种情况下,将标签2的X和Y对齐到父视图,以便将其固定在中心。然后将标签1的垂直空间设置为超级视图,并将标签2设置为标准,重复标签3。设置标签2后,设置标签1和3的最简单方法是调整它们的大小,直到它们对齐为止。这里是水平显示器,请注意,标签1和2之间的垂直空间已设置为标准空间:
这是纵向版本:
我意识到它们由于标签之间的标准空间与超级视图的标准空间之间存在差异,因此基线之间的间隔不是绝对100%相等。如果那样困扰您,请将大小设置为0,而不是标准
#25 楼
我仅为第一个项目设置宽度值(> =宽度),并为每个项目之间设置最小距离(> =距离)。然后,我使用Ctrl拖动第一个项目中的第二个,第三个...项目,以在项目之间建立依赖关系。#26 楼
聚会晚了,但是我有一个可行的解决方案,可以水平间隔创建菜单。使用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;
}
#27 楼
Android有一种在我想模仿的基于约束的布局系统中将视图链接在一起的方法。搜索将我带到这里,但没有一个答案奏效。我不想使用StackViews,因为它们比起预先保存起来往往使我更加悲痛。我最终创建了一个使用位于视图之间的UILayoutGuides的解决方案。通过控制宽度,可以使用Android的说法来分配不同类型的分布和链样式。该函数接受前导和尾随锚,而不是父视图。这允许将链放置在两个任意视图之间,而不是分布在父视图内部。它确实使用了仅在iOS 9+中可用的UILayoutGuide
,但这不再是问题。 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 { q4312078q.widthAnchor.constraint(equalToConstant: spacing) }
)
}
case .spread:
if let first = distributionGuides.first {
constraints.append(contentsOf:
distributionGuides.dropFirst().map { q4312078q.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 { q4312078q.widthAnchor.constraint(equalTo: key.widthAnchor) }
)
}
}
}
return constraints
}
#28 楼
我想水平对齐5张图像,所以我最终跟随Mete的响应而有一个小的差异。第一张图像将在容器中水平居中,该容器等于0,乘数为1:5:
第二个图像将在等于0且乘数为3:5的容器中水平居中:
其余的图像。例如,第五张(也是最后一张)图像将在容器中水平居中,该容器等于0并且乘数为9:5:
如Mete所解释的,该阶数为1、3 ,5、7、9等。位置遵循相同的逻辑:第一个位置为1,然后为空格,第二个位置为3,依此类推。
#29 楼
为什么不创建tableView并制作isScrollEnabled = false
评论
不了解投票否决权,我的解决方案是在框外思考。当可可在UITableView中为您提供列表时,为什么要烦恼使用自动布局创建列表呢?
–乔什·奥康纳(Josh O'Connor)
17年8月10日在17:41
评论
可能将一个标签的约束条件设置为另一个标签,并将它们的关系设置为“大于或等于”会很有帮助例如以编程方式使用此NSLayoutConstraint方法:+(id)constraintWithItem:(id)view1属性:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)与Item:(id)view2属性的关系:(NSLayoutAttribute)attr2乘数:(CGFloat)乘数常数:(CGFloat)c
我开源了一个通用的UITabBar替代品,该替代品使用Auto Layout概括了这种方法。您可以检查测试以查看如何生成自动版式约束。 GGTabBar