38

iOS 社区中似乎有几个不同的选项/术语和人们在布局方面使用(例如 UIEdgeInsets 是一种类型,但有时我听到/阅读“设置插图”或布局边距与布局指南)。

我总是能够找到一个可行的选择。但我永远不确定我是否使用了正确的工具来完成这项工作。

有人可以帮助在布局的这些不同方面以及何时以最佳方式使用它们之间提供一些清晰度吗?

4

3 回答 3

54

在阅读本文之前和之后,请参阅此UIKit:适用于各种尺寸和形状的应用程序。可能还想从这里查看笔记。

作为赏金提供者...我想说我的大部分困惑来自没有正确理解UILayoutGuide课程。那是关键,但也很简单。

我先介绍一个问题

在过去,如果您需要像这样限制这些圆圈:

在此处输入图像描述

然后您必须创建清晰的 UIViews 并将它们添加为您的子视图,然后将您的约束添加到它们,如下所示:

在此处输入图像描述


今天您不需要将它们添加为您的子视图。你可以改为使用

布局指南

要创建布局指南,您必须执行以下步骤:

  1. 实例化一个新的布局指南。
  2. 通过调用视图的addLayoutGuide(_:)方法将布局指南添加到视图。
  3. 使用自动布局定义布局指南的位置和大小。您可以使用这些指南来定义布局中元素之间的空间。以下示例显示了用于定义一系列视图之间的相等间距的布局指南。

脚步:

let space1 = UILayoutGuide()
view.addLayoutGuide(space1)
 
let space2 = UILayoutGuide()
view.addLayoutGuide(space2)
 
space1.widthAnchor.constraintEqualToAnchor(space2.widthAnchor).active = true
saveButton.trailingAnchor.constraintEqualToAnchor(space1.leadingAnchor).active = true
cancelButton.leadingAnchor.constraintEqualToAnchor(space1.trailingAnchor).active = true
cancelButton.trailingAnchor.constraintEqualToAnchor(space2.leadingAnchor).active = true
clearButton.leadingAnchor.constraintEqualToAnchor(space2.trailingAnchor).active = true

布局指南也可以充当黑匣子,包含许多其他视图和控件。这使您可以封装部分视图,将布局分解为模块化块。

三个有趣的注释:

  1. 如果您使用的是“查看调试层次结构”,那么您将看到更多的实例UILayoutGuide
  2. 就像一个 UIView,一个 UILayoutGuide实例各种各样的锚点
  3. 至于为什么不只是创建虚拟 UIViews 并通过创建 UILayoutGuides:“将虚拟视图添加到视图层次结构有很多成本。首先,创建和维护视图本身的成本。第二,虚拟视图"

在此处输入图像描述

有关更多信息,请参阅文档


topLayoutGuide对比safeAreaLayoutGuide

topLayoutGuide(已弃用)

它已被弃用,但出于学习目的:AUIViewController有 2 个虚拟框。顶部的 1 个属性命名为topLayoutGuide,底部的另一个属性命名为bottomLayoutGuide. viewController 本身没有任何左侧/前导或右/尾侧的指南。这两个都是一个例子UILayoutGuide

如果限制为 view.topAnchor 即:

tableView.topAnchor.constraint(equalTo: view.topAnchor)

在此处输入图像描述 tableView不是从导航栏的底部开始的。注意导航栏后面的橙色...

但是,如果您将其限制为topLayoutGuide.bottomAnchorie:

tableView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor) 

在此处输入图像描述

然后tableView从navigationBar的底部开始

根据您的布局设计,您可能希望您的内容在导航栏下方模糊。

这个想法是你可以边到边显示你的内容。它会在条形下方重叠,这样您就可以通过条形获得这些漂亮的彩色模糊与您的内容

有关更多信息,请参见WWDC 的这一刻和此处的此问题。我认为解决方案并不完全相关,只是问题中的图像。

安全区域布局指南

从iOS11开始

苹果已弃用topLayoutGuide& bottomLayoutGuidesafeAreaLayoutGuide因此,您现在有一个在 UIView 实例上命名的虚拟框,而不是两个虚拟框 。UIViewController 不再有任何这些......从useyourloaf复制的视觉比较: 在此处输入图像描述

旁注:如果您使用情节提要,那么将您的视图与 topLayoutGuide 或 safeAreaLayoutGuide 的顶部对齐将呈现相同的效果。如果您不使用故事板(以编程方式执行),那么您将不得不在 iOS11 和 LessThaniOS11 之间跳舞,并且有 2 个不同版本的代码

有关更多信息safeAreaLayoutGuide,我强烈建议您将Apple 的文章设置为:相对于安全区域定位内容

注意: safeAreaLayoutGuide是一个 UIView 属性。topLayoutGuide是一个 UIViewController 属性。


layoutMarginsGuide

  • UIView只有 1 个虚拟盒子。该属性名为layoutMarginsGuide。但不像UIViewController它不位于顶部或底部。它只是位于中心,在UIView. 那么这在哪里有用呢?如果你希望你的 textView 被限制在 UIView 实例的边缘,你可以使用它。这将改善阅读体验。或者,不是将按钮约束到其父视图的leadingAnchor 并使其看起来很难看,而是向锚点添加8 个点……即将按钮约束到leadingAnchor,然后添加8 个常量点。删除的文本实际上是您将使用的位置readableContentGuidelayoutMarginsGuide如果您不希望按钮或标签锚定时很有用到它的超级视图的边缘

      someButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8)
    

    但是等等有一个更简单的方法。只需使用 Apple 推荐的边距,即使用:

      someButton.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor)
    

    另请参阅中提供的示例

文档。一个很好的 Raywenderlich 教程可以在 这里找到

readableContentGuide

  • 与 略有不同layoutMarginGuide。两者都是 UIView 的属性。有时它们是相同的,有时它们不是。它的目的是:

    该布局指南定义了一个易于阅读的区域,而无需强迫用户移动头部来跟踪线条

    有关更多信息,请参阅WWDC 的这一刻:构建自适应布局和这个很棒的 useyourloaf 教程

    在 iPhone 7 Plus 的纵向上,可读的内容指南是相同的

作为视图的边距指南,但在横向中,文本视图的两侧都有更多的空白。在 iPad 上横屏时,留白空间明显增加。

边距大小取决于系统的动态类型。字体越大,指南越宽。

来自雷文德利希

在下图中,青色锚定在 上layoutMarginGuide,而绿色锚定在 上readableContentGuide

在此处输入图像描述

UIEdgeInsets

如果您想更改您的layoutMarginsGuideie,将所需的边距从 8 点更改为 16 点,那么您必须更改layoutMargins' 的值,然后layoutMarginsGuide' 的锚点会自动更新。 UIEdgeInsets只是你的类型layoutMargins。是类layoutMargins的属性UIView

someview.layoutMargins = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50)

我发现这段代码☝️唯一起作用的地方是里面viewDidLayoutSubviews。更多信息请看这里

Anchors

它们是基础但没有什么特别之处。它们是任何 UIView/UILayoutGuide 的最远边缘。UIView 和 UILayoutGuide 实例都有它。您约束的一切最终都会使用锚点进行约束,这只是您将其锚定到哪个实体的锚点的问题。它可能是一个safeAreaLayoutGuide锚点,它可能是一个layoutMarginGuide锚点,它可能是一个锚点,它可能是一个topLayoutGuide锚点view。(尽管您也可以将您的 heightAnchor 锚定为 50,因此在这种情况下没有另一个锚点)

和之间的一个很好的视觉比较可以从这个答案中找到。它是使用故事板完成的,因此更容易理解。layoutMarginsGuideAnchors


contentInsets

虽然理解很重要,但这是一个完全不同的讨论,与其他讨论无关。它特定于 UIScrollViews。有关更多信息,请参阅这篇很棒的文章


结论

为了安全起见,一切都您的视野之内,请使用safeAreaLayoutGuide. 如果您想使用系统提供的边距来获得更好的视图布局或有一些填充,那么请使用layoutMarginGuide,如果您想让事情更具可读性readableContentGuide

readableContentGuide大小总是小于或等于layoutMarginGuide
layoutMarginGuide大小总是小于或等于safeAreaLayoutGuide

layoutMargins与 CSS 的 padding 非常相似。safeAreaLayoutGuide类似于 CSS 边距。我不知道是否有任何 CSS 等价物readableContentGuide


附录:ContentInset 与 contentOffset

它们用于滚动视图,与答案的其余部分有些无关。至于什么是contentInset& contentOffset,请看WWDC 2018 的这一刻: UIKit: Apps for Every Size and Shape 。视频非常简单。另请参阅下面的 Karthik 的回答。话虽如此,您必须完全了解 scrollView 的工作原理并了解contentSize它是什么,否则它会很复杂。有关 scrollView 的更多信息contentSize,请在此处查看Vacawama 的回答

于 2017-12-03T01:26:27.437 回答
7

我希望您能从以下链接/图片中获得信息。

您将能够从下面的链接中推断出有关布局参数的所需信息。

  1. 对齐矩形。 对齐矩形。

  2. 内容插入和偏移 示例 2 内容插图 内容不合另一个例子

  3. 边距 边距

于 2017-11-30T09:48:15.733 回答
1

抱歉,如果这是一个无聊的答案,但我觉得 Apple Developer 文档非常擅长描述每个 UIView 属性的用途。

至于当您有多种方法/选项有效时,实现布局的最佳方式是什么……这确实是风格/意见的问题。我会专注于以下标准来做出决定:

  1. 考虑消除最难维护/调试/理解的选项
    • 想象一下有人全新加入您的团队或有人继承您的代码——他们将难以调试哪种实现,为什么?
  2. 考虑消除使布局更难重新排列/扩展/编辑的选项
  3. 考虑消除与应用程序其余部分不一致的选项(这种回到第一点)
  4. 考虑消除违背 Apple 意图的选项(请参阅 docs/WWDC 会谈以了解他们的意图)。

一个团队可以通过这个标准一起工作,以在每个场景中就“我们如何布局我们的 UI”达成一致。我想您要的是此类讨论的总和产品:各种布局样式指南。

根据我的经验,这总是在我工作过的团队中有机地发展起来,而且事情并没有真正写下来。上面的第 3 点还有很多内容。


问题中描述的类的 Apple 文档摘录:

UILayoutGuide 上

使用布局指南来替换您可能创建的虚拟视图,以表示用户界面中的视图间空间或封装。传统上,有许多需要虚拟视图的自动布局技术。

...

UILayoutGuide 类旨在执行以前由虚拟视图执行的所有任务,但以更安全、更有效的方式执行。

layoutMargins上:

在 iOS 11 及更高版本中,使用 directionalLayoutMargins 属性而不是此属性来指定布局边距。

directionalLayoutMargins 上

使用此属性可指定此视图的边缘与其子视图之间的所需空间量(以点为单位)。根据当前布局方向,将前导边距和后边距适当地应用于左边距或右边距。

contentInset 上

使用这个属性来扩展你的内容和内容视图边缘之间的空间。大小的单位是点。默认值为 UIEdgeInsetsZero。

topAnchor 上

使用此锚点创建具有视图顶部边缘的约束。您只能将此锚点与其他 NSLayoutYAxisAnchor 锚点结合使用。有关详细信息,请参阅 NSLayoutAnchor。

NSLayoutAnchor 上

使用这些约束以编程方式使用 Auto Layout 定义您的布局。不要直接创建 NSLayoutConstraint 对象,而是从您希望约束的 UIView、NSView 或 UILayoutGuide 对象开始,然后选择该对象的锚属性之一。这些属性对应于 Auto Layout 中使用的主要 NSLayoutConstraint.Attribute 值,并提供适当的 NSLayoutAnchor 子类来创建对该属性的约束。使用锚的方法来构造你的约束。

于 2017-11-28T21:33:31.817 回答