ios 在容器视图中均匀间隔多个视图
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13075415/
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
Evenly space multiple views within a container view
提问by nothappybob
Auto Layout is making my life difficult. In theory, it was going to be really useful when I switched, but I seem to fight it all of the time.
自动布局让我的生活变得困难。从理论上讲,当我切换时它会非常有用,但我似乎一直在与它作斗争。
I've made a demo project to try to find help. Does anyone know how to make the spaces between views increase or decrease evenly whenever the view is resized?
我制作了一个演示项目来尝试寻求帮助。有谁知道如何在调整视图大小时使视图之间的空间均匀增加或减少?
Here are three labels (manually spaced vertically even):
这是三个标签(手动垂直均匀间隔):
What I want is for them to resize their spacing (not the view size) evenly when I rotate. By default, the top and bottom views squish towards the center:
我想要的是让他们在我旋转时均匀地调整它们的间距(而不是视图大小)。默认情况下,顶部和底部视图向中心挤压:
采纳答案by Spencer Hall
So my approach allows you to do this in interface builder. What you do is create 'spacer views' that you have set to match heights equally. Then add top and bottom constraints to the labels (see the screenshot).
所以我的方法允许您在界面构建器中执行此操作。您所做的是创建您已设置为相同高度的“间隔视图”。然后向标签添加顶部和底部约束(参见屏幕截图)。
More specifically, I have a top constraint on 'Spacer View 1' to superview with a height constraint of lower priority than 1000 and with Height Equals to all of the other 'spacer views'. 'Spacer View 4' has a bottom space constraint to superview. Each label has a respective top and bottom constraints to its nearest 'spacer views'.
更具体地说,我对“Spacer View 1”有一个最高限制,以具有低于 1000 的优先级高度约束并且高度等于所有其他“间隔视图”的超级视图。'Spacer View 4' 对超级视图有一个底部空间限制。每个标签对其最近的“间隔视图”都有各自的顶部和底部约束。
Note: Be sure you DON'T have extra top/bottom space constraints on your labels to superview; just the ones to the 'space views'. This will be satisfiable since the top and bottom constraints are on 'Space View 1' and 'Spacer View 4' respectively.
注意:请确保您的标签上没有额外的顶部/底部空间限制以进行超级查看;只是“空间视图”的那些。这将是可满足的,因为顶部和底部约束分别位于“Space View 1”和“Spacer View 4”。
Duh 1: I duplicated my view and merely put it in landscape mode so you could see that it worked.
Duh 1:我复制了我的视图,只是把它放在横向模式下,这样你就可以看到它起作用了。
Duh 2: The 'spacer views' could have been transparent.
Duh 2:“间隔视图”可能是透明的。
Duh 3: This approach could be applied horizontally.
Duh 3:这种方法可以横向应用。
回答by smileBot
LOOK, NO SPACERS!
看,没有空格!
Based on suggestions in the comments section of my original answer, especially @Rivera's helpful suggestions, I've simplified my original answer.
根据我原始答案的评论部分中的建议,尤其是@Rivera 的有用建议,我简化了我的原始答案。
I'm using gifs to illustrate just how simple this is. I hope you find the gifs helpful. Just in case you have a problem with gifs, I've included the old answer below with plain screen shots.
我正在使用 gif 来说明这是多么简单。我希望你觉得这些 gif 有帮助。以防万一您对 gif 文件有疑问,我在下面提供了带有简单屏幕截图的旧答案。
Instructions:
指示:
1)Add your buttons or labels. I'm using 3 buttons.
1)添加您的按钮或标签。我正在使用 3 个按钮。
2)Add a center x constraint from each button to the superview:
2)将每个按钮的 center x 约束添加到超级视图:
3)Add a constraint from each button to the bottom layout constraint:
3)从每个按钮添加一个约束到底部布局约束:
4)Adjust the constraint added in #3 above as follows:
4)调整上面#3中添加的约束如下:
a)select the constraint, b)remove the constant (set to 0), c)change the multiplier as follows: take the number of buttons + 1, and starting at the top, set the multiplier as buttonCountPlus1:1, and then buttonCountPlus1:2, and finally buttonCountPlus1:3. (I explain where I got this formula from in the old answer below, if you're interested).
a)选择约束, b)删除常量(设置为 0), c)更改乘数如下:取按钮数 + 1,从顶部开始,将乘数设置为buttonCountPlus1:1,然后buttonCountPlus1 :2,最后是buttonCountPlus1:3。(如果您有兴趣,我会在下面的旧答案中解释我从哪里得到这个公式的)。
5)Here's a demo running!
5)这是一个正在运行的演示!
Note: If your buttons have larger heights then you will need to compensate for this in the constant value since the constraint is from the bottom of the button.
注意:如果你的按钮有更大的高度,那么你需要在常量值中对此进行补偿,因为约束来自按钮的底部。
Old Answer
旧答案
Despite what Apple's docs and Erica Sadun's excellent book (Auto Layout Demystified) say, it is possible to evenly space views withoutspacers. This is very simple to do in IB and in code for any number of elements you wish to space evenly. All you need is a math formula called the "section formula". It's simpler to do than it is to explain. I'll do my best by demonstrating it in IB, but it's just as easy to do in code.
尽管 Apple 的文档和 Erica Sadun 的优秀书籍 ( Auto Layout Demystified) 说了些什么,但无需间隔器就可以均匀地间隔视图。这在 IB 和代码中非常简单,可用于您希望均匀间隔的任意数量的元素。您所需要的只是一个称为“截面公式”的数学公式。做起来比解释起来简单。我将尽我所能在 IB 中演示它,但在代码中也同样容易。
In the example in question, you would
在有问题的例子中,你会
1) start by setting each label to have a center constraint. This is very simple to do. Just control drag from each label to the bottom.
1)首先将每个标签设置为具有中心约束。这很简单。只需控制从每个标签到底部的拖动即可。
2) Hold down shift, since you might as well add the other constraint we're going to use, namely, the "bottom space to bottom layout guide".
2)按住shift键,因为您还可以添加我们将要使用的另一个约束,即“底部空间到底部布局指南”。
3) Select the "bottom space to bottom layout guide", and "center horizontally in container". Do this for all 3 labels.
3) 选择“底部空间到底部布局指南”和“在容器中水平居中”。对所有 3 个标签执行此操作。
Basically, if we take the label whose coordinate we wish to determine and divide it by the total number of labels plus 1, then we have a number we can add to IB to get the dynamic location. I'm simplifying the formula, but you could use it for setting horizontal spacing or both vertical and horizontal at the same time. It's super powerful!
基本上,如果我们取我们希望确定其坐标的标签并将其除以标签总数加 1,则我们可以将一个数字添加到 IB 以获得动态位置。我正在简化公式,但您可以使用它来设置水平间距或同时设置垂直和水平间距。它超级强大!
Here are our multipliers.
这是我们的乘数。
Label1 = 1/4 = .25,
标签 1 = 1/4 = .25,
Label2 = 2/4 = .5,
标签 2 = 2/4 = .5,
Label3 = 3/4 = .75
标签 3 = 3/4 = .75
(Edit: @Rivera commented that you can simply use the ratios directly in the multiplier field, and xCode with do the math!)
(编辑:@Rivera 评论说,您可以直接在乘数字段中直接使用比率,而 xCode 可以进行数学运算!)
4) So, let's select Label1 and select the bottom constraint. Like this:
4) 所以,让我们选择 Label1 并选择底部约束。像这样:
5) Select the "Second Item" in the Attributes Inspector.
5) 在属性检查器中选择“第二项”。
6) From the drop down select "Reverse first and second item".
6) 从下拉菜单中选择“反转第一项和第二项”。
7) Zero out the constant and the wC hAny value. (You could add an offset here if you needed it).
7) 将常数和 wC hAny 值清零。(如果需要,您可以在此处添加偏移量)。
8) This is the critical part: In the multiplier field add our first multiplier 0.25.
8) 这是关键部分:在乘数字段中添加我们的第一个乘数 0.25。
9) While you're at it set the top "First item" to "CenterY" since we want to center it to the label's y center. Here's how all that should look.
9)当你在它上面时,将顶部的“第一项”设置为“CenterY”,因为我们希望将其居中到标签的 y 中心。这就是这一切的样子。
10) Repeat this process for each label and plug in the relevant multiplier: 0.5 for Label2, and 0.75 for Label3. Here's the final product in all orientations with all compact devices! Super simple. I've been looking at a lot of solutions involving reams of code, and spacers. This is far and away the best solution I've seen on the issue.
10) 对每个标签重复这个过程并插入相关的乘数:Label2 为 0.5,Label3 为 0.75。这是具有所有紧凑型设备的所有方向的最终产品!超级简单。我一直在研究很多涉及大量代码和间隔符的解决方案。这是我在这个问题上看到的最好的解决方案。
Update: @kraftydevil adds that Bottom layout guide only appear in storyboards, not in xibs. Use 'Bottom Space to Container' in xibs. Good catch!
更新:@kraftydevil 补充说底部布局指南只出现在故事板中,而不是在 xibs 中。在 xibs 中使用“底部空间到容器”。接得好!
回答by Mete
Very quick Interface Builder solution:
非常快速的界面生成器解决方案:
For any number of views to be evenly spaced within a superview, simply give each an "Align Center X to superview" constraint for horizontal layout, or "Align Center Y superview" for vertical layout, and set the Multiplier to be N:p
(NOTE: some have had better luck with p:N
- see below)
对于在超级视图中均匀分布的任意数量的视图,只需为水平布局提供“对齐中心 X 到超级视图”约束,或为垂直布局提供“对齐中心 Y 超级视图”约束,并将乘数设置为N:p
(注意:有些有更好的运气p:N
- 见下文)
where
在哪里
N = total number of views
, and
N = total number of views
, 和
p = position of the view including spaces
p = position of the view including spaces
First position is 1, then a space, making the next position 3, so p becomes a series [1,3,5,7,9,...]. Works for any number of views.
第一个位置是 1,然后是一个空格,使得下一个位置是 3,所以 p 变成了一个序列 [1,3,5,7,9,...]。适用于任意数量的视图。
So, if you have 3 views to space out, it looks like this:
所以,如果你有 3 个视图可以隔开,它看起来像这样:
EDITNote: The choice of N:p
or p:N
depends on the relation order of your alignment constraint. If "First Item" is Superview.Center, you may use p:N
, while if Superview.Center is "Second Item", you may use N:p
. If in doubt, just try both out... :-)
编辑注意:N:p
或的选择p:N
取决于对齐约束的关系顺序。如果“First Item”是 Superview.Center,则可以使用p:N
,而如果 Superview.Center 是“Second Item”,则可以使用N:p
。如果有疑问,请尝试两者... :-)
回答by Glorfindel
As of iOS 9, Apple has made this very easy with the (long-awaited) UIStackView
. Just select the views you want to contain in the Interface Builder and choose Editor -> Embed In -> Stack view. Set the appropriate width/height/margin constraints for the stack view, and make sure to set the Distribution property to 'Equal spacing':
从 iOS 9 开始,Apple 通过(期待已久的)UIStackView
. 只需选择您想要包含在 Interface Builder 中的视图,然后选择 Editor -> Embed In -> Stack view。为堆栈视图设置适当的宽度/高度/边距约束,并确保将 Distribution 属性设置为“等间距”:
Of course, if you need to support iOS 8 or lower, you'll have to choose one of the other options.
当然,如果您需要支持 iOS 8 或更低版本,则必须选择其他选项之一。
回答by jrturton
I've been on a rollercoaster ride of loving autolayout and hating it. The key to loving it seems to be to accept the following:
我一直在喜欢自动布局和讨厌它的过山车上。爱它的关键似乎是接受以下几点:
- Interface builder's editing and "helpful" auto-creation of constraints is near useless for all but the most trivial case
- Creating categories to simplify common operations is a life-saver since the code is so repetitive and verbose.
- 界面构建器的编辑和“有用的”自动创建约束对除最微不足道的情况外几乎无用
- 创建类别以简化常见操作是一种救命稻草,因为代码是如此重复和冗长。
That said, what you are attempting is not straightforward and would be difficult to achieve in interface builder. It is pretty simple to do in code. This code, in viewDidLoad
, creates and positions three labels how you are asking for them:
也就是说,您正在尝试的操作并不简单,并且很难在界面构建器中实现。在代码中实现非常简单。这段代码,在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]];
As I say, this code is much simplified with a couple of category methods in UIView
, but for clarity I've done it the long way here.
正如我所说,这段代码通过 中的几个类别方法得到了极大的简化UIView
,但为了清楚起见,我在这里做了很长时间。
The category is herefor those interested, and it has a method for evenly spacing an array of views along a particular axis.
类别是这里对于那些有兴趣,并且其具有用于沿特定轴均匀间隔的视图的阵列的方法。
回答by Ben Dolman
Most of these solutions depend on there being an odd number of items so that you can take the middle item and center it. What if you have an even number of items that you still want to be evenly distributed? Here's a more general solution. This category will evenly distribute any number of items along either the vertical or horizontal axis.
这些解决方案中的大多数都依赖于奇数个项目,以便您可以取中间项目并将其居中。如果您仍然希望均匀分布偶数个项目怎么办?这是一个更通用的解决方案。此类别将沿垂直或水平轴均匀分布任意数量的项目。
Example usage to vertically distribute 4 labels within their superview:
在其父视图中垂直分布 4 个标签的示例用法:
[self.view addConstraints:
[NSLayoutConstraint constraintsForEvenDistributionOfItems:@[label1, label2, label3, label4]
relativeToCenterOfItem:self.view
vertically:YES]];
NSLayoutConstraint+EvenDistribution.h
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
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
回答by Oleh Kudinov
The correct and easiest way is to use Stack Views.
正确且最简单的方法是使用堆栈视图。
- Add your labels/views to the Stack View:
- 将您的标签/视图添加到Stack View:
- Select the Stack View and set Distributionto be Equal Spacing:
- 选择 Stack View 并将Distribution设置为Equal Spacing:
- Add Spacing to nearest neighborconstraints to the Stack View and update frames:
- 将间距添加到最近邻约束到堆栈视图并更新帧:
- Add Heightconstraints to all the labels (optional). Needed only for views that does not have Intrinsic Size). Labels for example does not need here height constrains and only need to set numberOfLines = 3 or 0 for example.
- 为所有标签添加高度约束(可选)。仅在没有固有大小的视图中需要)。例如标签在这里不需要高度约束,例如只需要设置 numberOfLines = 3 或 0。
- Enjoy Preview:
- 欣赏预览:
回答by smileyborg
Check out the open source library PureLayout. It offers a few API methods for distributing views, including variants where the spacing between each view is fixed (view size varies as needed), and where the size of each view is fixed (spacing between views varies as needed). Note that all of these are accomplished withoutthe use of any "spacer views".
查看开源库PureLayout。它提供了一些用于分发视图的 API 方法,包括每个视图之间的间距固定(视图大小根据需要变化)和每个视图的大小固定(视图之间的间距根据需要变化)的变体。请注意,所有这些都是在不使用任何“间隔视图”的情况下完成的。
From 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;
// ...
Since it's all open source, if you're interested to see how this is achieved without spacer views just take a look at the implementation. (It depends on leveraging both the constant
andmultiplier
for the constraints.)
由于它都是开源的,如果您有兴趣了解如何在没有间隔视图的情况下实现这一点,请查看实现。(这取决于对约束constant
和multiplier
约束的利用。)
回答by Florian
I am having a similar problem and discovered this post. However, none of the currently provided answers solve the problem in the way you want. They don't make the spacing equally, but rather distribute the center of the labels equally. It is important to understand that this is not the same. I've constructed a little diagram to illustrate this.
我遇到了类似的问题并发现了这篇文章。但是,当前提供的答案都没有以您想要的方式解决问题。它们不会使间距相等,而是平均分配标签的中心。重要的是要了解这不一样。我已经构建了一个小图表来说明这一点。
There are 3 views, all 20pt tall. Using any of the suggested methods equally distributes the centers of the views and give you the illustrated layout. Notice that the y-center of the views are spaced equally. However, the spacing between superview and top view is 15pt, while the spacing between the subviews is just 5pt. To have the views spaced equally these should both be 10pt, i.e. all blue arrows should be 10pt.
有 3 个视图,都是 20pt 高。使用任何建议的方法平均分布视图的中心并为您提供插图布局。请注意,视图的 y 中心间隔相等。但是,superview 和 top view 之间的间距是 15pt,而 subviews 之间的间距只有 5pt。为了使视图间隔相等,这些都应该是 10pt,即所有蓝色箭头都应该是 10pt。
Nevertheless, I haven't come up with a good generic solution, yet. Currently my best idea is to insert "spacing views" between the subviews and setting the heights of the spacing views to be equal.
尽管如此,我还没有想出一个好的通用解决方案。目前我最好的想法是在子视图之间插入“间距视图”并将间距视图的高度设置为相等。
回答by hayesk
I was able to solve this entirely in IB:
我能够在 IB 中完全解决这个问题:
- Make constraints to align the center Y of each of your subviews to the bottom edge of the superview.
- Set the multiplier of each of these constraints to 1/2n, 3/2n, 5/2n, …, n-1/2n where n is the number of subviews you are distributing.
- 进行约束以将每个子视图的中心 Y 与超级视图的底部边缘对齐。
- 将这些约束中的每一个的乘数设置为 1/2n、3/2n、5/2n、...、n-1/2n,其中 n 是您要分发的子视图的数量。
So if you have three labels, set the multipliers to each of those constraints to 0.1666667, 0.5, 0.833333.
因此,如果您有三个标签,请将每个约束的乘数设置为 0.1666667、0.5、0.833333。