【问题标题】:Autolayout - equal distribution of 6 views自动布局 - 6 个视图的平均分布
【发布时间】:2013-10-20 01:29:52
【问题描述】:

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

  • 两个顶部按钮与超级视图的垂直距离应相同 (A)
  • 两个底部 - 相同 (C)
  • 中间两个的中心应该在 superview 的中心线上
  • 所有按钮 (E) 之间的垂直距离应该相同
  • 最后但并非最不重要的一点 - 按钮应该是方形的(因此宽度和高度应该相同)
  • A = C
  • B = D

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

【问题讨论】:

  • 我认为在 IB 中是可能的。即使很难,我也会用代码完成所有布局。 Xcode 不能很好地处理 xib。
  • 但是 - 你如何设置 width=height 约束,并将所有按钮设置为相同的值?
  • [myView addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeHeight multiplier:1.0f constant:0.0f]];
  • 您为一个按钮设置宽度和高度,然后如果您从一个按钮控制拖动到另一个按钮,则会出现“等宽”和“等高”选项
  • 这里有一个问题,所有按钮都需要彼此具有相同的高度和宽度吗?如果他们这样做,那么你的条件不能总是满足:如果 A != C,“中间的两个应该有他们的中心在 superview 的中心线”不能为真。

标签: xcode interface-builder autolayout


【解决方案1】:

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

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

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

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

    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:|[容器1][容器2(==容器1)]| H:|[container3(==container1)][container4(==container1)]| H:|[container5(==container1)][container6(==container1)]| V:|[container1][container3(==container1)][container5(==container1)]| V:|[container2(==container1)][container4(==container1)][container6(==container1)]|

    然后您可以在其中添加按钮,在六个小容器中的每一个上居中一个,然后使您的容器清晰:

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

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

    请参阅 WWDC 2015 视频What's New in Cocoa Touch

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

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

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

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

【讨论】:

  • 这个答案很彻底,很棒!
【解决方案2】:

更新:我有一个更好的解决方案,它不使用垫片。看看here

好的,这在 IB 中可以很快实现。就是这么简单。这是一个有助于说明的图表。

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

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

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

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

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

但是当我旋转时,哎呀!

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

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

并明确了间隔:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-02
    • 1970-01-01
    • 2014-12-17
    • 1970-01-01
    • 2014-01-18
    • 1970-01-01
    相关资源
    最近更新 更多