xcode 自动布局 - 平均分配 6 个视图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19327095/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 04:05:00  来源:igfitidea点击:

Autolayout - equal distribution of 6 views

xcodeinterface-builderautolayout

提问by kender

I want to have 6 objects (buttons) laid out inside one view. They should, however, follow some constraints:

我想在一个视图中布置 6 个对象(按钮)。但是,它们应该遵循一些限制条件:

enter image description here

在此处输入图片说明

  • Two top buttons should have the same vertical distance from superview (A)
  • Two bottom - the same (C)
  • Two in the middle should have their centers at the superview's center line
  • The vertical distances between all buttons (E) should be the same
  • and last but not least - the buttons should be square (so the width and height should be the same)
  • A= C
  • B= D
  • 两个顶部按钮与超级视图的垂直距离应相同 ( A)
  • 两个底-相同 ( C)
  • 中间的两个应该在superview的中心线上有他们的中心
  • 所有按钮 ( E)之间的垂直距离应相同
  • 最后但并非最不重要的 - 按钮应该是方形的(所以宽度和高度应该相同)
  • A= C
  • B= D

Is it possible to have this effect just in the IB, or should I use some additional code for the constraints?

是否可以仅在 IB 中产生这种效果,还是应该使用一些额外的代码来约束?

回答by Rob

This is a logical request, but constraints are defined using the attributes of views, but cannot not be defined in relation to other constraints. That having been said, there are a number of approaches:

这是一个逻辑请求,但约束是使用视图的属性定义的,但不能相对于其他约束来定义。话虽如此,有多种方法:

  1. Layout guides:An approach which doesn't require predetermining the any spacing is to have UILayoutGuideobjects or, if using iOS versions before 9, just use hidden views, i.e. views with clear background or alpha of zero, in between the buttons.

    The idea is to add these layout guides with addLayoutGuide(or add invisible views with addSubviewif supporting iOS versions predating iOS 9) in between your six buttons as "spacers", and define the spacers to be the same size as each other, and with constraints between the spacers, the superview, and the buttons that will go in between the spacer. Once you lay that out (showing the horizontal spacer views in blue, vertical ones in red, just so you can see them):

    spacer views

    The equivalent VFL for the constraints for those red UIViewobjects, called vspacerX, would be:

    H:|[vspacer1][button1(100)][vspacer2(==vspacer1)][button2(==button1)][vspacer3(==vspacer1)]|
    H:|[vspacer1][button3(==button1)][vspacer2][button4(==button1)][vspacer3]|
    H:|[vspacer1][button5(==button1)][vspacer2][button6(==button1)][vspacer3]|
    

    And constraints on the blue UIViewobjects, called hspacerX, like:

    V:|[hspacer1][button1(100)][hspacer2(==hspacer1)][button3(==button1)][hspacer3(==hspacer1)][button5(==button1)][hspacer4(==hspacer1)]|
    V:|[hspacer1][button2(==button1)][hspacer2][button4(==button1)][hspacer3][button6(==button1)][hspacer4]|
    

    You don't have to use VFL to define these constraints, as any way you define these constraints will work, but it's just a concise format for describing the collection of constraints that I employed.

    Anyway, when the view is rendered with those layout guides (or invisible views), it yields evenly spaced buttons like so:

    spacers hidden

  2. Another approach is to have six "container" views, that would look like:

    containers

    The equivalent VFL for these six container UIViewobjects might look like:

    H:|[container1][container2(==container1)]|
    H:|[container3(==container1)][container4(==container1)]|
    H:|[container5(==container1)][container6(==container1)]|
    
    V:|[container1][container3(==container1)][container5(==container1)]|
    V:|[container2(==container1)][container4(==container1)][container6(==container1)]|
    

    You can then add your buttons to that, centering one on each of the six little containers and then make your containers clear:

    just buttons

    This works, too, but just a slightly different spacing (where the margins are half of the spacing between the views, whereas the other approach keeps the margins the same as the spacing between them.

  3. Stack view:In a permutation of the prior point, in iOS 9, you can also use UIStackView, designed precisely for evenly spacing views. In this case, put two buttons each in three horizontal stack views, and then place those stack views within a vertical stack view. This achieves six evenly sized container views.

    See WWDC 2015 video What's New in Cocoa Touch.

    The problem with stack views is that they can be used to ensure even spacing between the arranged subviews, they don't ensure spacing before the first arranged view nor after the last arranged view. So, the kludge to get around that is to, for horizontal stack view, include two more zero width views (or zero height for vertical stack views). Then when you use even spacing on the stack view, it also give you what will appear to be spacing before and after all of the arranged subviews.

  4. NSLayoutAttributeCenterXwith multiple:Another technique involves defining the attribute:NSLayoutAttributeCenterXand attribute:NSLayoutAttributeCenterYattributes for your six buttons, but rather than using the constantvalues, use the multiplierfield. This technique enjoys a little simplicity, but doesn't always render the desired effect, so I won't describe it unless it's something you definitely want to pursue. I've already entered tl:dr territory here.

  5. Collection view:Another approach is to use a UICollectionView, which handles this scenario gracefully. It's well designed to let you layout cells in a grid.

  6. Hardcoding values:For the sake of completeness, I'll note that you could simply specify specific values for A, B, C, and D (as well as the width and height constraints). You don't even have to worry about setting the E constraints, but rather just set the vertical center constraint of the middle two to their superview, and you're effectively done (because the spacing represented by E should be a natural result of the previous steps, assuming A=C and B=D). If you want to adjust these values on the basis of device size and/or orientation, you can then implement a viewWillLayoutSubviewsto adjust the constants for these constraints according to the size of the view.

  1. 布局指南:一种不需要预先确定任何间距的方法是UILayoutGuide在按钮之间放置对象,或者,如果使用 9 之前的 iOS 版本,则只需使用隐藏视图,即具有清晰背景或 alpha 为零的视图。

    这个想法是在你的六个按钮之间添加这些布局指南addLayoutGuideaddSubview如果支持 iOS 9 之前的 iOS 版本,则添加不可见的视图)作为“间隔”,并将间隔定义为彼此相同的大小,并在它们之间添加约束垫片、超级视图和将在垫片之间的按钮。布置好后(以蓝色显示水平间隔视图,以红色显示垂直间隔视图,以便您可以看到它们):

    间隔视图

    这些红色UIView对象的约束的等效 VFL ,称为vspacerX,将是:

    H:|[vspacer1][button1(100)][vspacer2(==vspacer1)][button2(==button1)][vspacer3(==vspacer1)]|
    H:|[vspacer1][button3(==button1)][vspacer2][button4(==button1)][vspacer3]|
    H:|[vspacer1][button5(==button1)][vspacer2][button6(==button1)][vspacer3]|
    

    以及对蓝色UIView对象的约束,称为hspacerX,例如:

    V:|[hspacer1][button1(100)][hspacer2(==hspacer1)][button3(==button1)][hspacer3(==hspacer1)][button5(==button1)][hspacer4(==hspacer1)]|
    V:|[hspacer1][button2(==button1)][hspacer2][button4(==button1)][hspacer3][button6(==button1)][hspacer4]|
    

    您不必使用 VFL 来定义这些约束,因为您定义这些约束的任何方式都可以工作,但这只是一种描述我使用的约束集合的简明格式。

    无论如何,当使用这些布局指南(或不可见视图)呈现视图时,它会产生均匀间隔的按钮,如下所示:

    隐藏的垫片

  2. 另一种方法是拥有六个“容器”视图,如下所示:

    容器

    这六个容器UIView对象的等效 VFL可能如下所示:

    H:|[container1][container2(==container1)]|
    H:|[container3(==container1)][container4(==container1)]|
    H:|[container5(==container1)][container6(==container1)]|
    
    V:|[container1][container3(==container1)][container5(==container1)]|
    V:|[container2(==container1)][container4(==container1)][container6(==container1)]|
    

    然后你可以添加你的按钮,将一个按钮放在六个小容器中的每一个上,然后让你的容器清晰:

    just buttons

    这也有效,但只是间距略有不同(其中边距是视图之间间距的一半,而另一种方法保持边距与它们之间的间距相同。

  3. 堆栈视图:在前一点的排列中,在 iOS 9 中,您还可以使用UIStackView,专为均匀间隔视图而设计。在这种情况下,将两个按钮分别放在三个水平堆栈视图中,然后将这些堆栈视图放置在垂直堆栈视图中。这实现了六个大小均匀的容器视图。

    请参阅 WWDC 2015 视频Cocoa Touch 中的新增功能

    堆栈视图的问题在于它们可用于确保排列的子视图之间的间距均匀,它们不能确保第一个排列视图之前或最后一个排列视图之后的间距。因此,解决这个问题的方法是,对于水平堆栈视图,包括另外两个零宽度视图(或垂直堆栈视图的零高度)。然后,当您在堆栈视图上使用均匀间距时,它还会为您提供所有排列的子视图之前和之后的间距。

  4. NSLayoutAttributeCenterXwith multiple另一种技术涉及为您的六个按钮定义attribute:NSLayoutAttributeCenterXattribute:NSLayoutAttributeCenterY属性,但不是使用constant值,而是使用multiplier字段。这个技巧有点简单,但并不总能呈现出想要的效果,所以我不会描述它,除非它是你肯定想要追求的东西。我已经在这里进入了 tl:dr 领域。

  5. 集合视图:另一种方法是使用 a UICollectionView,它可以优雅地处理这种情况。它经过精心设计,可让您在网格中布置单元格。

  6. 硬编码值:为了完整起见,我会注意到您可以简单地为 A、B、C 和 D(以及宽度和高度约束)指定特定值。您甚至不必担心设置 E 约束,而只需将中间两个的垂直中心约束设置为它们的超视图,您就有效地完成了(因为 E 表示的间距应该是前面的步骤,假设 A=C 和 B=D)。如果您想根据设备大小和/或方向调整这些值,则可以实现 aviewWillLayoutSubviews以根据视图的大小调整这些约束的常量。

回答by smileBot

Update: I have a better solution that does not use spacers. Check it out here.

更新:我有一个更好的解决方案,它不使用垫片。检查它在这里

Ok, this can be achieved very quickly in IB. It's so so simple. Here's a diagram that will help illustrate. enter image description here

好的,这可以在 IB 中非常快地实现。太简单了。这是一个有助于说明的图表。 enter image description here

Assume v1-6 are your buttons, and s1-5 are your spacers. 1) in IB control drag out all of the connections shown by the red lines.

假设 v1-6 是您的按钮,而 s1-5 是您的垫片。1)在IB控件中拖出红线所示的所有连接。

2) shift click v1-6 and pin icon (looks like |-I-| ) set the width and height to a definite value. also, set the height and width to be equal.

2) shift click v1-6 和 pin icon(看起来像 |-I-| )将宽度和高度设置为一个确定的值。另外,将高度和宽度设置为相等。

3) shift select s1-4 (not 5) and set the height to equal. do not give it a definite height, since this should be calculated by the system. you might also need to set the widths of s1-4 to be equal, but don't give them a definite width.

3)移位选择s1-4(不是5)并将高度设置为相等。不要给它一个明确的高度,因为这应该由系统计算。您可能还需要将 s1-4 的宽度设置为相等,但不要给它们一个确定的宽度。

4) control drag from the centre views to the leading and trailing edge and set the centre constraint.

4)控制从中心视图到前缘和后缘的拖动并设置中心约束。

So, you might think, ok, this should work now. It doesn't. Here's my app running in portrait with slightly different colors. Looks good. (Notice, you would make the spacers invisible once you get it setup).

所以,你可能会想,好吧,现在应该可以了。它没有。这是我的应用程序以略有不同颜色的纵向运行。看起来挺好的。(请注意,一旦设置好垫片,您就会使其不可见)。

enter image description here

enter image description here

But when I rotate, oops!

但是当我旋转时,哎呀!

enter image description here

enter image description here

What's happening here? The problem is incredibly easy to solve once we understand what's gone wrong. What we want is for IB to not shrink our views. We want IB to make the spacers and the spaces to shrink and grow as necessary, but to leave our views alone. Basically, IB has shrunk the spacers down as far as it can in portrait and to attempt to make everything fit IB has shrunk our views. But we wanted IB to shrink the vertical spaces between views and spacers, not our views. The solution is so easy. All we have to do is adjust the priority of the vertical spaces and all is well. So, select the vertical spaces in IB and adjust the priority to 750. The vertical spacing lines will show as dashed. Done.

这里发生了什么事?一旦我们了解出了什么问题,这个问题就非常容易解决。我们想要的是IB不要缩小我们的观点。我们希望 IB 使间隔物和空间根据需要收缩和增长,但不要管我们的意见。基本上,IB 已经尽可能地缩小了纵向间隔,并试图让一切都适合 IB 缩小了我们的视野。但是我们希望 IB 缩小视图和间隔之间的垂直空间,而不是我们的视图。解决方法很简单。我们所要做的就是调整垂直空间的优先级,一切都很好。因此,在IB中选择垂直间距并将优先级调整为750。垂直间距线将显示为虚线。完毕。

enter image description here

enter image description here

enter image description here

enter image description here

Ok, so here's everything as we expect it.

好的,这就是我们所期望的一切。

enter image description here

enter image description here

And with the spacers made clear:

并明确了垫片:

enter image description here

enter image description here

enter image description here

enter image description here