【问题标题】:Auto Layout (Constraints) Center 2 side by side views in a parent view自动布局(约束)在父视图中居中 2 并排视图
【发布时间】:2013-01-22 09:32:24
【问题描述】:

我试图弄清楚如何使用自动布局 (iOS6) 和约束来做到这一点。

基本上,我将大视图分为底部的两个部分。在这些部分(当前为子视图)中,我有一个图像视图和一个标签。我想在两边居中,使用可变长度的文本。

我的大部分时间都围绕着自动布局,但我不确定最好的方法。我倾向于认为这在 IB 中是不可能的,但在代码中。

将继续尝试解决这个问题,但与此同时,这是我正在尝试创建的示例。

【问题讨论】:

    标签: iphone ios constraints autolayout


    【解决方案1】:

    这就是你所追求的吗?

    我通过在您的 leftSection 中添加一个视图(名为 viewCenteredInLeftSection),然后将时钟图像和标签添加为具有这些约束的子视图来做到这一点:

    • 使viewCenteredInLeftSection 的CenterX 和CenterY 等于其父视图的(leftSection)。
    • 使clockImage 的顶部、底部和前边缘等于其父视图的 (viewCenteredInLeftSection)。
    • 使label 的后沿等于其父视图的 (viewCenteredInLeftSection)。
    • clockImage 的后缘设为距label 前缘的标准距离。

    我在 Interface Builder 中调整 iOS UIView 的大小时遇到​​了问题,因此我为 OS X 制作了我的示例,并且完全可以在 Interface Builder 中完成。如果您在 Interface Builder 中制作上述约束时遇到问题,请告诉我,我将发布创建它们的代码。

    2014-08-26 编辑

    Luda,这里是 Xcode 5 的 Pin 和 Align 菜单,也可以在 Xcode 的菜单栏中找到:

    下面是我的示例在 Interface Builder 中的样子。蓝色视图是原始问题的“父视图”,即图像和标签应居中的给定视图。

    我添加了绿色视图(我将其命名为viewCenteredInLeftSection)作为“父视图”的子视图。然后我突出显示它并使用对齐菜单“容器中的水平中心”和“容器中的垂直中心”来创建约束来定义其位置。

    我将时钟图像添加为viewCenteredInLeftSection 的子视图,并带有定义其宽度和高度的约束。我突出显示了时钟图像和viewCenteredInLeftSection,然后使用对齐 > 前缘、对齐 > 顶边和对齐 > 底边应用了约束。

    我将标签添加为viewCenteredInLeftSection 的子视图,将其定位为时钟图像中的standard Aqua space distance。我突出显示了标签和 viewCenteredInLeftSection,然后使用 Align > Trailing Edges 应用了约束。

    与 Xcode 4 相比,使用 Xcode 5 的 Interface Builder 更容易创建。

    【讨论】:

    • 是的,这就是我所追求的。这是一个很好的解决方案,但是通过使用代码,我无需添加额外的视图就可以做到这一点。我很快就会在这里发布代码。
    • 我发现添加一个封装 UIView 比通过代码完成要容易得多。干得好!
    • “使clockImage的顶部、底部和前沿等于其父视图的”是什么意思?这可以在 Interface Builder 中完成吗?
    • @JohnSauer 你的意思可能是“对齐 -> 后沿”(不是尾随图像)
    • 这是唯一不需要编码的解决方案,甚至适用于宽度不相等的 UI 元素。占位符视图是关键,而不是设置此视图的宽度,而是设置约束以便它根据其子元素调整其视图是我自己唯一无法弄清楚的事情。谢谢!
    【解决方案2】:

    我想出了一个不添加另一个视图的方法:

     [aView addConstraint:[NSLayoutConstraint constraintWithItem:viewOnLeft attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationLessThanOrEqual toItem:aView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
     [aView addConstraint:[NSLayoutConstraint constraintWithItem:viewOnRight attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationLessThanOrEqual toItem:aView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
    

    您还可以更改常量以在视图之间创建间隙。

    • 左视图约束常量:-X
    • 右视图约束常量:+X

    【讨论】:

    • 非常有趣。很好的解决方案!
    • 其实经过更多的检查,我不太确定你在这里做什么。这两个子视图周围是否至少有一个包装器?介意分享一个示例项目或详细说明您的解释吗?
    • 没有包装,只有“aView”,它是包含白球和计数器的矩形。 'viewOnLeft' 是白球,'viewOnRight' 是计数器。
    • 啊,我明白你在说什么。好的,这是一个非常酷的解决方案。现在我想你甚至可以在没有代码中的“aView”包装的情况下做到这一点。
    • 这似乎将左视图的右侧和右视图的左侧与其容器的中心对齐。如果左侧视图和右侧视图的宽度大致相同,这看起来很好。但是,如果一侧明显比另一侧宽,它肯定会看起来不平衡。
    【解决方案3】:

    斯坦福大学关于 ios 7 的讲座中考虑了一个解决方案。它工作得很好。在此附上该解决方案。 (这里 sdfssfg... 是 label1 而 efsdfg.... 是 label2)

    【讨论】:

    • 愿意提供进一步的说明吗?
    • 基本上用两个透明的 uiviews(比如说 V1 和 V2)附加那些所需的两个标签(比如说 L1 和 L2)。V1 和 V2 应该具有相等的宽度,并且所有四个视图应该对齐为:- V1-L1-L2-V2-。这里的连字符是它们之间的水平距离(约束)可以是任何值。我会建议保持为零。
    • 我怎样才能找到那个讲座?它的号码是多少?
    • 查看此视频youtube.com/watch?v=pv1EHGEf884(第48分钟后他将解释上述问题)
    • 这里给出了更简化的答案,例如stackoverflow.com/questions/15590196/…
    【解决方案4】:

    我花了一点时间,但我想出了一个非常可靠的解决方案。我想出了 John Sauer 提供的相同解决方案,但不想添加另一个视图来包装这些。

    答案需要三个步骤。

    1) 包含其他两个子视图的宽度(我将其称为leftInfoSection)需要由其内容确定。这消除了它对超级视图(或其他视图)具有左右约束以确定其宽度的需要。这是一个真正的关键,其中很多东西是让孩子定义宽度。

    2) 我仍然必须在 IB 中有一个领先的约束,它才能有一个有效的布局。 (它需要知道在哪里水平放置leftInfoSection)。将该约束连接到您的代码中,以便您可以删除它。除此之外,我还有一个尾随约束 GTE 垂直分隔线 + 3。

    3) 最后一个关键是考虑您必须使用哪些信息(在代码中,因为 IB 是有限的)。我意识到我知道我的部分上方水平分隔线的中心,并且我的leftInfoSection 的中心将是该水平条的中心减去水平条宽度的 1/4。下面是左右两边的最终代码:

    // remove the unwanted constraint to the right side of the thumbnail
    [self.questionBox removeConstraint:self.leftInfoViewLeadingConstraint];
    // calculate the center of the leftInfoView
    CGFloat left = self.horizontalDividerImageView.frame.size.width/4 * -1;
    // constrain the center of the leftInfoView to the horizontal bar center minus a quarter of it to center things
    [self.questionBox addConstraint:[NSLayoutConstraint constraintWithItem:self.leftInfoView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.horizontalDividerImageView attribute:NSLayoutAttributeCenterX multiplier:1 constant:left]];
    
    // remove the unwanted constraint to the right side of the questionBox
    [self.questionBox removeConstraint:self.rightInfoViewTrailingConstraint];
    // calculate the center of the rightInfoView
    CGFloat right = left * -1;
    // constrain the center of the rightInfoView to the horizontal bar center plus a quarter of it to center things
    [self.questionBox addConstraint:[NSLayoutConstraint constraintWithItem:self.rightInfoView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.horizontalDividerImageView attribute:NSLayoutAttributeCenterX multiplier:1 constant:right]];
    

    结果:

    此外,IB 可能对它自动更新约束的方式非常烦人。当我试图将子视图上的前导和尾随约束定义为 0 时,它会不断断开一个或另一个并对父视图进行约束以定义宽度。诀窍是暂时保留不需要的约束,但将其优先级降低到 999。然后我可以创建子视图约束来定义宽度。

    【讨论】:

      【解决方案5】:

      这很好用,但需要 2 个间隔 UIView:

      UIView *spacer1 = [[UIView alloc] init];
      spacer1.translatesAutoresizingMaskIntoConstraints = NO;
      [self.view addSubview:spacer1];
      
      UIView *spacer2 = [[UIView alloc] init];
      spacer2.translatesAutoresizingMaskIntoConstraints = NO;
      [self.view addSubview:spacer2];
      
      NSDictionary *views = NSDictionaryOfVariableBindings(spacer1, spacer2, imageView, label);
      
      [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[spacer1(>=0)][imageView]-4-[label][spacer2(==spacer1)]|" options:0 metrics:nil views:views];
      
      for (int i = 0; i < constraintsArray.count; i++) {
      
          [self.view addConstraint:constraintsArray[i]];
      }
      

      【讨论】:

      • ... 并且,为了记录,我无法通过简单地添加imageView.leading = label.trailing 的约束来完成这项工作。可能我太厚了。
      【解决方案6】:

      在 iOS 9 之后,实现此目的的另一种选择是使用 Stack Views

      【讨论】:

      • 适用于 iOS 9.0 及更高版本
      • 一个例子会是一个更好的答案。
      【解决方案7】:

      有几种方法可以做到这一点。基本来说,这里是如何将 1..n 个项目居中,假设您的所有项目都有大小限制并且不会增长

      1. 在物品的每一侧放置 2 个垫片。将垫片锚定到父边。将您的第一个和最后一个项目锚定到锚点上。最后,指定 1 个垫片具有另一个垫片的宽度。您不需要显式设置任何间隔大小,因为它将被解决

        • spacer1 -> left=parent:left width=spacer2:width
        • spacer2 -> 右=父:右
        • yourFirstItem -> left=spacer1:right
        • yourLastItem -> right=spacer2:left
      2. 如果垫片不是你的东西,并且你和你有奇数个项目,则将中间一个放在父项的中心。另外,请确保第一个和最后一个项目没有锚定到父边缘。

        • yourMiddleItem = centerX=parent:centerX
        • otherItems->yourMiddleItem
      3. 如果垫片不是您的东西,并且您有偶数个项目,请将 2 个内部项目的边缘居中到父项的中心。另外,请确保第一个和最后一个项目没有锚定到父边缘。

        • leftMiddleItem -> right=parent:centerX
        • rightMiddleItem -> left=parent:centerX
        • otherItems->leftMiddleItem rightMiddleItem
      4. 您也可以在中心放置一个不可见的占位符,并锚定到该位置,但在约束时仍需要考虑奇数/偶数项,因此我不推荐这种方法。

      【讨论】:

        【解决方案8】:

        你可能想参考这个

        Percentage based marking

        基本上,它说首先使用一些不正确的边距,然后将其更正到其父视图。

        处理我的案例。

        【讨论】:

          猜你喜欢
          • 2019-08-28
          • 1970-01-01
          • 2019-11-24
          • 2017-10-09
          • 1970-01-01
          • 2015-07-31
          • 2016-11-20
          • 2016-06-27
          • 1970-01-01
          相关资源
          最近更新 更多