swift layout_Swift Auto-Layout的原因,内容和方式

swift layout

I think people are a lot like UI components on a phone. In a way, we’re all trying to find our place in this bezel-less screen we call life.

我认为人们很喜欢手机上的UI组件。 在某种程度上,我们都试图在我们称之为生活的无边框屏幕中找到自己的位置。

We want to find where we’re meant to be in this world, where we can grow and fill the space around us with our potential, or where we can settle to make room for others.

我们想找到我们在这个世界上的定位,我们可以在这里成长并利用我们的潜力填补周围空间,或者我们可以为其他人腾出空间的地方。

We might not ever fill these longings in life, but at the very least, we can find solace in knowing that iOS components using Auto-Layout have found their place in theirs.

我们可能永远都无法满足生活中的这些渴望,但是至少,我们可以从使用自动布局的iOS组件中找到自己的位置中找到安慰。

We’re going to take a look at why Auto-Layout exists, how it works, and some ways we can use it to configure dynamic UI (before things like VStacks, Spacers, and TikTok existed).

我们将了解为什么存在自动布局,其工作方式以及一些使用它来配置动态UI的方式(在存在诸如VStacks,Spacers和TikTok之类的东西之前)。

You’ll see that working with Auto-Layout not only teaches us how to make rectangles show up on a screen, but also leaves us a few life lessons along the way. Get comfortable, play some lo-fi, and let’s embark on this introspective journey together 🧘🏽‍♂️ —

您会看到,使用“自动布局”不仅教会了我们如何使矩形显示在屏幕上,而且还为我们提供了一些生活上的教训。 放松一下,玩些低保真音响,让我们一起踏上这一内省之旅吧。

为什么要自动布局? 😶 (Why Auto-Layout? 😶)

To fully understand what Auto-Layout is, we first have to look at the problem it’s trying to solve.

要完全了解什么是自动版式,我们首先必须查看它要解决的问题。

Frames 🖼

框架🖼

Originally, views give superviews context by setting an explicit position and size (a frame) in order to place themselves on-screen.

最初,视图通过设置显式位置和大小 ( 框架 )来将其置于屏幕上,从而提供了超级视图上下文。

The superview takes this context and tells its coordinate system exactly where and how large it should display.

超级视图会采用此上下文,并告诉其坐标系统确切的位置以及显示的大小。

What we’re going to do — Using an iPhone 8, we want to display two identically sized squares next to each other and fill to screen width. We’re going to apply 20 points of padding to the left and right side of the squares, as well as 20 points in between:

我们要做的是-使用iPhone 8,我们要彼此相邻显示两个大小相同的正方形,并填充到屏幕宽度。 我们要申请20 正方形左侧和右侧的填充点,以及之间的20个点:

Justifications:An iPhone 8’s point dimensions are 375 x 667view1 x-origin = 20 (Full left padding)view2 x-origin = 375/2 + 10 = 197.5 (Centered with half-padding)Both width/height = 375/2 - 20 - 10 = 157 
(Half the screen width without left and right side paddings)
Both y-origins = 250 (Arbitrary for our requirements)

After applying the most math I’ve done since refinancing my student loans, we find exactly what origin and dimension values we need in order to make the context for our superview.

应用了自为学生贷款再融资以来我所做的最多的数学工作之后,我们确切地找到了为构成超级视图的上下文所需的原点和尺寸值。

We’ll then create a CGRect frame with these values to instantiate our UIViews with. Now when we add these views to any arbitrary superview, that superview will use this frame to lay it out in its own coordinate system.

然后,我们将创建一个CGRect使用这些值框架来实例化UIViews用。 现在,当我们将这些视图添加到任何任意超级视图时,该超级视图将使用此框架将其布局在其自己的坐标系中

It doesn’t matter at which point in the code we add these views as subviews, since it’s the views themselves that holds the information of their frames.

在代码中的哪一点上,我们都可以将这些视图添加为子视图,因为视图本身保存着其框架的信息。

We can technically add these subviews until the last configuration lines and it’ll still display correctly!

从技术上讲,我们可以添加这些子视图,直到最后一个配置行,它仍然可以正确显示!

Now that we have our views, let’s run the app and see how it goes —

现在我们有了自己的见解,让我们运行该应用程序,看看它如何进行-

Figure 1.A
iPhone 8 (375 x 667)
iPhone 8(375 x 667)

Looks good 👍🏽

看起来不错👍🏽

Since we were specific in where and how large to place view1 and view2, we see that the superview sets them as expected.

由于我们特定于放置view1view2的位置和大小,因此我们可以看到超级视图按预期设置了它们。

If there’s no anticipated changes in the UI, then each subview’s frame will always be relevant to the context of its superview’s coordinate system.

如果UI中没有预期的更改,则每个子视图的框架将始终与其父视图的坐标系的上下文相关。

But what happens when the superview’s frame changes, or even of one of its subviews? Let’s run the app again with the same values, but this time with an iPhone 11 Max

但是,当超级视图的框架甚至其子视图之一发生变化时,会发生什么? 让我们以相同的值再次运行该应用,但是这次使用iPhone 11 Max

Image for post
iPhone 11 Pro Max (414 x 896)
iPhone 11 Pro Max(414 x 896)

Oh no 🤭

哦no

Since the superview’s frame changed from 375x667 to 414x896, our original calculations based on the iPhone 8 dimensions failed to scale.

由于超级视图的帧从375x667更改为414x896 ,因此我们基于iPhone 8尺寸的原始计算无法缩放。

Sure we can make a constant for the width value to something like the UIScreen’s width, but what if the culprit of the change is the subview’s content? 🙊

当然,我们可以将width值设为一个常数,例如UIScreen的宽度,但是如果改变的罪魁祸首是子视图的内容怎么办? 🙊

We won’t know how to accommodate the subview’s frame from the super’s perspective without some serious effort.

如果不付出很多努力,我们将不知道如何从超级视图的角度适应子视图的框架。

And as self-proclaimed “good programmers”, we hate effort. Specially the serious kind.

作为自称“优秀程序员”的我们讨厌付出努力。 特别是认真的一种。

Accommodating phone sizes, anticipating dynamic view content, or even adjusting for landscape mode can be very annoying when recursively calculating the frames in your view hierarchy.

在递归计算视图层次结构中的帧时,适应电话大小,预测动态视图内容甚至调整横向模式都可能很烦人。

- ()

什么是自动版式? 💀 (What is Auto-Layout exactly? 💀)

That’s why Auto-Layout exists. By using constraints to represent the relationship between views, Auto-Layout generates these frames for you. It calculates the size and position on your behalf as long as the rules you set (the constraints) result in one sound outcome.

这就是存在自动布局的原因。 通过使用约束来表示视图之间的关系,自动版式会为您生成这些框架。 只要您设置的规则(约束)产生一个合理的结果,它就会代表您计算大小和位置。

Constraints and Attributes ⛓

约束和属性⛓

In short, constraints are the relationship between one or two participating view’s edges, sizes, or centers, usually with some sort of modification like an offset or multiple. They’re the building blocks of Auto-Layout.

简而言之, 约束条件是一个或两个参与视图的边缘,大小或中心之间的关系,通常会进行某种修改,例如偏移或倍数。 它们是自动版式的基础。

To represent the ‘something’ that pins views together, we sort them into distinct categories. We can just call the ‘somethings’ attributes.

为了表示将视图固定在一起的“东西”,我们将它们分为不同的类别。 我们可以称其为“事物”属性。

Attribute Categories —Horizontal: Left, Right, Leading, Trailing, and Center-XVertical: Top, Bottom, and Center-YSize: Height and Width

Note: Leading and Trailing are functionally similar to Left and Right, but can change depending on device locale. A leading anchor is left to the view in USA locales, but right in Japanese locales. Vice-versa for trailing. Use them instead of left and right constraints if you need to localize your app for these locales. 🔀

注意: 前导和尾随 在功能上类似于 Left和Right ,但是可以根据设备的区域设置而改变。 在美国区域设置中,主要锚定留在视图中,而在日本区域设置中,则保留主视图。 反之亦然。 如果您需要为这些语言环境本地化应用程序,请使用它们代替左右约束。 🔀

Now that we have our attributes, let’s take a look at applying them! I like to think of the anatomy of a constraint in roughly three parts 🍡 —

现在我们有了属性,让我们看一下应用它们! 我喜欢在大约三个部分中思考约束的解剖结构-

Participants: The one or two participating attribute(s)Inequalities: The type of relationship between participant(s)Modification: Some sort of optionally applied defined change

You can also only set constraints between ones in each category, meaning you can’t pin a view’s top constraint to another view’s left and so on.

您还只能在每个类别的约束之间设置约束,这意味着您不能将视图的顶部约束固定到另一个视图的左侧,依此类推。

Let’s take a look at how a constraint is made with a basic NSLayoutConstraint. Referencing our squares example, we’re going to pin the left side of the first square to the left side of the superview:

让我们看一下如何使用基本的NSLayoutConstraint进行约束。 参考我们的正方形示例,我们将第一个正方形的左侧固定到超级视图的左侧:

Note: In order to let Auto-Layout know you wish to use this constraint, you need to set the constraint’s isActive property to true.

注意:为了让自动布局知道您希望使用此约束,需要将约束的 isActive 属性设置为 true

  • The participants 👬 in this constraint are view1 and its superview (the view controller’s view).

    此约束中 参与者are view1 及其 超级 视图(视图控制器的视图)。

  • The inequality ⚖️ is indicated by the relatedBy parameter, and in this case set to equal, but can also be set to lessThanOrEqual or greaterThanOrEqual. This means that if a constraint needs to grow or shrink due to some frame change, you’re giving it permission to do so.

    不等式 ⚖️ 由relatedBy参数所指示,并且在这种情况下,设定为 equal ,但也可以设定为 lessThanOrEqual greaterThanOrEqual 这意味着,如果某个约束由于某些框架更改而需要增大或缩小,则可以授予它这样做的权限。

  • The modifications 🏎 in this constraint is an offset of 20, meaning view1’s left is going to be equal to view’s left, but shifted to the right by 20 points. The multiplier is also a modification, but since we’re not dealing with sizes, it doesn’t make sense to change it.

    此约束中 修改an 的偏移量为20,这意味着 view1 的左侧将等于 view1 的左侧,但向右移动20个点。 乘数也是一个修改,但是由于我们不处理大小,因此更改它没有意义。

There’s one cardinal rule when it comes to working with constraints. They all have to play nice with each other, meaning constraints generally need to implicitly convey the position and size of your view, instead of explicitly like our frames did.

处理约束时有一个基本规则。 它们都必须相互配合,这意味着约束通常需要隐式传达视图的位置和大小,而不是像我们的框架那样显式地表达

The superview still needs a size and position for their subviews, so constraints need to provide that information. The only difference is that information is given through relationships, not through hard numbers. Because why do math when you can do not-math? 💩

超级视图仍然需要其子视图的大小和位置,因此约束需要提供该信息。 唯一的区别是信息是通过关系而不是硬数字提供的。 因为为什么数学不能做的时候为什么呢? 💩

The game plan — Our constraints for our original example will look like this:

游戏计划-我们对原始示例的约束如下所示:

Left square:The left attribute must equal the superview’s left + 20 pointsThe top attribute must equal the superview’s top + 250 pointsThe width must equal the super's width divided by 2 - 30 pointsThe height attribute must equal its own width attributeRight square:The left attribute must equal the left square’s right + 20 pointsThe height and width attribute must equal the left’sThe centerY attribute must equal the left square’s centerY attribute

Note: Since the coordinate system for any view originates from the top-left corner (where origin = CGPoint(x: 0, y: 0)), all positive offsets will shift a constraint in the down/right direction, while negative offsets shift in the up/left direction.

注意:由于任何视图的坐标系都起源于左上角(原点= CGPoint(x:0,y:0)),因此所有正偏移量将沿上下方向移动约束,而负偏移量将偏移在上/左方向。

Let’s break this down per object —

让我们按对象细分一下-

Left Square:

左方:

  • We define the size by indicating the width as equal to the super’s width along with an offset of -20 points applied, and the height equal to its own width (or equal to the same constraint our width depends on).

    我们 通过指示 宽度 等于super的宽度并加上-20点的偏移量来 定义 大小 ,并且 高度 等于其自身宽度(或等于我们的宽度所依赖的相同约束)。

  • We define the position by indicating it must equal to the super’s left attribute (x-axis) with a 20 point positive offset to shift it to the right, and equal to the super’s top attribute (y-axis) with a 250 point positive offset to shift it down the screen.

    通过 定义 位置 ,它必须等于super的左属性 (x轴)( 向右偏移20点 ) 才能向右移动,并等于super的top属性 (y轴)( 带250点正偏移)将其向下移动到屏幕上。

Right Square:

右方:

  • We can imply where the position is because we know how far left (y-axis) it needs to be by making its left attribute equal to the other square’s right attribute (with padding), as well as how for down (x-axis) by making its y-center attribute equal to the left square’s y-center.

    我们可以暗示 位置 在哪里, 因为我们知道 它需要 离开的最左端 (y轴) ,方法是使它的left属性等于另一个正方形的right属性(带有填充),以及如何向下 (x轴) 通过使其y中心属性等于左方的y中心。

  • We know the size of it by setting its width and height constraint equal to the left’s size.

    我们知道, 它通过设置其 宽度 高度 约束 大小 等于左边的大小。

No rules are being broken by these relationships, but let’s say we leave out the second requirement for the right square. Now Auto-Layout doesn’t know how large the right square needs to be anymore.

这些关系并没有违反任何规则,但是可以说,我们省略了正确平方的第二个要求。 现在,“自动版式”不知道需要再多大的正方形。

It doesn’t play nice. This is going to cause some issues during run-time, since in most cases the compiler won’t throw a fit over some missing constraints. We give the position of the right square, but not the size. Constraints need to fulfill both, since it’s going to be rendered down to frames later on! 😮

播放效果不佳。 这将在运行时引起一些问题,因为在大多数情况下,编译器不会对某些缺失的约束进行调整。 我们给出右方的位置 ,但不给出大小 。 约束需要同时满足这两个条件,因为稍后将渲染为帧! 😮

- ()

我们如何实现自动版式? 🤭 (How do we implement Auto-Layout? 🤭)

There’s plenty of tools when it comes to Auto-Layout. Through trial, error, and deprecation, we find a set of practical approaches for modern day layout development —

关于自动布局,有很多工具。 通过反复试验,错误和过时,我们发现了一套实用的现代布局开发方法,

  • We can do it programmatically, with NSLayoutAnchor (the successor of NSLayoutConstraint after iOS 8*)

    我们可以做到这一点编程,与 NSLayoutAnchor(NSLayoutConstraint iOS 8的后继任 *)

  • We can do it visually, with Interface Builder (IB) and Storyboards

    我们可以使用 Interface Builder 直观地做到这一点 (IB) 和情节提要

  • Or we can even take a non-native approach with third-party kits and DSLs like SnapKit, which is built on top of native constraints

    或者,我们甚至可以 对第三方工具包和 SnapKit 等DSL 采用非本机方法 ,该 工具包 是基于本机约束构建的

*Since we’re currently at iOS 13, let’s assume we’ll just need to run iOS 10+

* 由于我们目前使用的是iOS 13,因此假设我们只需要运行iOS 10+

Picking the right Auto-Layout tool can be tricky. We need to really reflect on why Apple and friends felt the need to give us so many (it could be for no reason honestly). But there probably is a reason! So which ones do we choose? Which ones Josh?? What was the reason?? WHAT WAS THE REASON??

选择正确的“自动布局”工具可能会很棘手。 我们需要真正思考一下为什么苹果和朋友觉得有必要给我们这么多(坦白地说,这可能是毫无原因的)。 但是可能是有原因的! 那么我们选择哪些呢? 那些乔什? 是什么原因? 原因是什么?

NSLayoutAnchors 🧘🏽‍♂️ (NSLayoutAnchors 🧘🏽‍♂️)

You’ve got to find yourself first. Everything else’ll follow — Charles De Lint

您必须先找到自己。 其他所有内容都会跟进-Charles De Lint

You know how I mentioned that constraints need to have participants in the same attribute category? Before NSLayoutAnchors, its predecessor NSLayoutConstraint didn’t have any checks on the constraint category of its attribute parameters.

您知道我提到约束需要参与者属于同一属性类别吗? 在NSLayoutAnchors之前,其前身NSLayoutConstraint没有对其属性参数的约束类别进行任何检查。

This means that you can compile and run a constraint that pins a view’s right to another view’s top (which crashes your app)! 😬

这意味着您可以编译运行一个约束,将一个视图的权限固定到另一个视图的顶部(这会使您的应用程序崩溃)! 😬

To fix this, and to provide a more concise syntax, NSLayoutAnchors were given the power of self actualization. They were built with type-safety in mind and only sets constraints between attributes of the same category. 🤝

为了解决此问题并提供更简洁的语法, NSLayoutAnchors具有自我实现的能力 。 它们的构建考虑了类型安全性,并且仅在相同类别的属性之间设置约束。 🤝

The anatomy of an NSLayoutAnchor can be skimmed down to —

NSLayoutAnchor的解剖结构可以简化为-

<someView>.<someAnchor>.constraint(<someInequality>: <nextAnchor>)

Note: The someAnchor param and nextAnchor must be of the same category, or the compiler will throw up before you get the chance to run and crash —

注意: someAnchor 参数和 nextAnchor 必须属于同一类别,否则编译器将在您有机会运行并崩溃之前抛出错误—

Image for post

The parameters are also a subset of the full parameter list in NSLayoutConstraint, which means less code and less ambiguity.

参数还是NSLayoutConstraint完整参数列表的子集 ,这意味着更少的代码和更少的歧义。

You ask for what you just need, and if you need something extra, they have a full list of convenience inits to compensate for fewer parameters.

您只需要您需要的东西,如果您需要额外的东西,它们会提供完整的便捷初始化列表,以补偿较少的参数。

So back to the squares, but this time with our new friend NSLayoutAnchor ⚓️

回到广场,但是这次和我们的新朋友一起 NSLayoutAnchor ⚓️

There’s one thing we need to do before setting up our constraints. Since we don’t want autoresizing masks to interference with Auto-Layout in creating constraints, we want to set the participating views’ translatesAutoresizingMaskIntoConstraints values to false.

在设置约束之前,我们需要做一件事。 由于我们不希望在调整约束时自动调整蒙版大小以干扰自动布局,因此我们希望将参与视图的translatesAutoresizingMaskIntoConstraints值设置为false

view1.translatesAutoresizingMaskIntoConstraints = false

Let’s highlight some of the NSLayoutAnchor functionality above —

让我们重点介绍一下上面的NSLayoutAnchor功能-

Earlier we mentioned how parameters are in a need to know basis, so we see below that a constant parameter can be appended to the list if you need to add optional padding (which also compiles and runs without it)!

前面我们提到了需要如何了解参数的基础,因此下面我们看到,如果需要添加可选的填充(可以在没有填充的情况下进行编译和运行),则可以将constant参数附加到列表中!

view1.topAnchor.constraint(equalTo: view.topAnchor, 
constant: 250).isActive = true

Below, leadingAnchor and trailingAnchor are of the same category (horizontal), so the compiler accepts this as a valid constraint —

下面, leadingAnchortrailingAnchor都是同一个类别(水平),所以编译器接受这是一个有效的约束-

view2.leadingAnchor.constraint(equalTo: view1.trailingAnchor, 
constant: 20).isActive = true

Also along with constants, multipliers can be inserted in the parameter list in situations where you need a scaled relationship between two size attributes.

在需要两个大小属性之间的比例关系的情况下, multipliers也可以与常数一起插入参数列表中。

view1.widthAnchor.constraint(equalTo: view.widthAnchor, 
multiplier: 0.5,
constant: -30).isActive = true

We run the app and see that it displays perfectly on an iPhone 11 Pro Max! 🎉

我们运行该应用程序,发现它可以在iPhone 11 Pro Max上完美显示! 🎉

Image for post

故事板和IB🗺 (Storyboards & IB 🗺)

“If you can dream it, you can do it.” — Walt Disney

“如果可以梦想,就可以做到。” 沃尔特·迪斯尼

There’s a certain sense of gratification when you see your final product after spending so much time fine-tuning it. As someone whose attention span has been permanently ruined by modern media, I could care less.

当您花费大量时间对最终产品进行微调后看到最终产品时,就会有某种满足感。 作为一个注意力被现代媒体永久破坏的人,我可以不在乎。

I want that sweet-sweet instant gratification for what I’m doing right here, right now, yesterday, the day before, last year, before I was born —

我希望我现在在这里,昨天,前天,去年,我出生前在这里所做的一切都能得到我的甜美的即时满足,

That’s the cool thing about Storyboards: You see what you get. There’s no frills, you click and drag constraints, and Interface Builder (the editor that Storyboard runs on) updates the UI in real time!

这就是情节提要的最酷的事情:您看到了所得到的。 毫不费力,您只需单击并拖动约束,Interface Builder(运行Storyboard的编辑器)即可实时更新UI!

Image for post
Figure 1.A
图1.A

The view controller for this example has our two squares as subviews to its content view via IB.

此示例的视图控制器通过IB将我们的两个正方形作为其内容视图的子视图。

Constraints between views can be created by hovering over one view and clicking control and dragging to a second view (or itself).

可以通过将鼠标悬停在一个视图上并单击control并拖动到第二个视图(或本身)来创建视图之间的约束。

Release it and a tooltip appears where you can select the attribute(s) for the constraint created between these views, setting our participants.

释放它,然后会出现一个工具提示,您可以在其中选择在这些视图之间创建的约束的属性,从而设置参与者。

Once you create your constraint, you can also edit them in the interface on the right hand side in order to fine tune it. You’re given the option of changing your items (participants), changing your relation (inequalities), or changing your constants/multipliers (modifications), all within one convenient toolbox!

创建约束后,还可以在右侧的界面中对其进行编辑,以对其进行微调。 您可以选择在一个方便的工具箱中更改项目( 参与者 ),更改关系( 不等式)或更改常量/乘数( 修改 )!

Image for post
Figure 1.B
图1.B

Below is the same line of code in Figure 1.A that we implemented in IB with just one click and drag:

下面是我们在IB中只需单击并拖动即可实现的图1.A中的同一行代码:

view1.leadingAnchor.constraint(equalTo: view.leadingAnchor,
constant:
20).isActive = true

If you need more control of the constraint outside of IB, we can even create an IBoutlet if we need to update any properties (ie. isActive) during runtime in code —

如果您需要对IB之外的约束进行更多控制,如果我们需要在代码运行时在运行时更新任何属性(即isActive ),我们甚至可以创建IBoutlet

Image for post

We don’t even need to set the participating views’ translatesAutoresizingMaskIntoConstraints property since IB takes care of that for us.

我们甚至不需要设置参与视图的translatesAutoresizingMaskIntoConstraints属性,因为IB会为我们处理。

Let’s recreate all the constraints we have for our NSLayoutAnchor example in Storyboard. We see that the constraints we created via drag and drop appear on the right hand side of the editor under the constraints tab for that view—

让我们重新创建故事板中NSLayoutAnchor示例所具有的所有约束。 我们看到,通过拖放操作创建的约束出现在该视图的“约束”选项卡下的编辑器的右侧,

Image for post

The interface here is super helpful with its line guides. We see how each constraint is applied to the view, even with the padding indicated. ✨

此处的界面通过其线路指南非常有用。 我们看到了如何将每个约束应用于视图,即使指定了填充也是如此。 ✨

Image for post

This is a very powerful visual tool that we don’t get through programmatic approaches. If we have any conflicting constraints, IB lets us know right away by highlighting in red any breaking constraints.

这是一个非常强大的可视化工具,我们无法通过编程方法来获得。 如果我们有任何冲突的约束,IB会通过用红色突出显示任何突破性约束立即通知我们。

Storyboards have their own set of benefits over a programmatic approach.

与编程方法相比,情节提要具有其自身的优点。

  • Navigation is explicitly indicated, making your view controller flows have visual representations.

    明确显示了导航,使视图控制器流具有视觉表示。

  • You will write significantly less code, making this method super appealing for codebases that suffer from view layers that require more UI logic.

    您将编写 更少的代码 ,从而使该方法对于遭受需要更多UI逻辑的视图层的代码库 具有极大的 吸引力。

  • Because of that, it’s a great fit for design patterns that preach about single responsibility. (MVVM, VIPER, MVLMNOP, QRS, TUV)

    因此,它非常适合宣传单一职责的设计模式。 (MVVM,VIPER,MVLMNOP,QRS,TUV)

Note: Storyboards get transcribed to XML, and in some instances this might not play well with version control. This can definitely be mitigated by practices like using multiple storyboards, or not committing the automatic updates IB applies when you open it.

注意:情节提要板被转录为XML,在某些情况下,这可能不适用于版本控制。 可以通过使用多个情节提要板之类的方法,或者在打开时不提交IB适用的自动更新的方法来减轻这种情况。

We run the app and see (to no surprise) that the UI has rendered exactly how IB displayed —

我们运行了该应用程序,发现( 毫不奇怪 )UI完全呈现了IB的显示方式-

Image for post

SnapKit和第三方👉🏽👈🏽 (SnapKit & Third-Parties 👉🏽👈🏽)

There is no need to suffer silently and there is no shame in seeking help. — Catherine Zeta-Jones

无需默默受苦,寻求帮助也不会感到羞耻。 —凯瑟琳·泽塔·琼斯

I love SnapKit so much. If there weren’t any significant pros and cons between choosing a UI tool for a project, I’d go with SnapKit more times than I’d like to admit. 💩

我非常喜欢SnapKit。 如果为项目选择UI工具之间没有任何明显的利弊,那么与SnapKit相比,我选择的次数要多得多。 💩

Seems a bit excessive (and irresponsible), but the syntax is just so nice. Although I know.. No matter how nice a toolkit is, or how convenient it is to write layout code in fewer lines, SnapKit (and friends) are still third party.

似乎有点过分(而且不负责任),但是语法非常好 。 尽管我知道..无论工具包多么好,或者用更少的行编写布局代码有多方便,SnapKit(和朋友)仍然是第三方。

Bringing in a dependency to your app can be a big decision. If you choose one that ends up being unsupported in newer versions of iOS, it can tie down your app to the previously supported version until you find a solution. This forces you to either remove that dependency and implement a native fix, or fork the dependency and fix it yourself. 🤡

为您的应用程序带来依赖可能是一个重大决定。 如果您选择的一个版本最终在较新的iOS版本中不受支持,则它将您的应用程序绑定到以前受支持的版本,直到找到解决方案为止。 这迫使您要么删除该依赖关系并实施本机修复,要么派生该依赖关系并自行修复。 🤡

With first party solutions like NSLayoutAnchor readily available for recent iOS versions, it’s getting hard to justify going third party. Sure you get more out of the box with something like SnapKit, but I found that most of what you need is already within UIKit. (Nick if you’re reading this, I regret nothing)

随着诸如NSLayoutAnchor类的第一方解决方案可以在最新的iOS版本中使用,越来越难以证明要使用第三方。 当然,使用SnapKit之类的产品可以使您获得更多收益,但是我发现您需要的大多数东西已经在UIKit中了。 (如果您正在阅读这篇文章,尼克,我什么都不后悔)

And if there’s anything extra that’s missing in your app level toolkit, you can always write an extension or app level util to accommodate.

而且,如果您的应用程序级工具包中缺少任何其他东西,您可以随时编写extension或应用程序级实用程序来适应。

So with that disclaimer out of the way, we can get into why SnapKit is the best solution for any product, any use case, any iOS version in the past, present, and all future timelines that involves an iPhone.

因此,不用理会该免责声明,我们就可以了解为什么SnapKit是适用于任何产品,任何用例,过去,现在以及涉及iPhone的所有未来时间表的最佳解决方案。

The syntax for creating a constraint can be divided into the following —

创建约束的语法可以分为以下几种:

<someView>.snp.makeConstraints <Make Closure>

Each UIView will have a property called snp which is provided by SnapKit. You can either call makeConstraints, updateConstraints, or remakeConstraints.

每个UIView都有一个名为snp的属性,该属性由SnapKit提供。 您可以调用makeConstraintsupdateConstraintsremakeConstraints

As the name suggests, SnapKit not only lets you create constraints initially, but also lets you update all/remake a subset of the constraints indicated inside your Make Closure:

顾名思义,SnapKit不仅可以让您最初创建约束,而且还可以让您全部/重新制作Make Closure内部指示的约束子集:

Make Closure = ((MakeObject) -> Void)

After calling your intended constraint method, you’ll have to create your constraints inside the trailing closure provided.

调用了预期的约束方法后,您必须在提供的尾随闭包内部创建约束。

Note: All constraints inside of it will activate automatically, meaning you’ll have to create some sort of reference to your SnapKit constraint (the Constraint object) in order to call deactivate() (which is their way of setting the isActive property to false).

注意:其中的所有约束都会 自动 激活 ,这意味着您必须创建对SnapKit约束( Constraint 对象)的 某种引用 才能调用 deactivate() (这是将 isActive 属性 设置 false )。

Constraints in SnapKit follow a minimalist syntax —

SnapKit中的约束遵循简约的语法-

$0.<Attribute>.<Inequality>(<Attribute>).<Optional Modification>

You can set the participants, inequality, and optional modifications all in one short line. 😮

您可以在短短的一行中全部设置参与者不等式和可选修改 。 😮

Below, we recreate our squares example one last time —

下面,我们最后一次重新创建正方形示例-

There’s a lot of ~🆒~ things going on here.

这里有很多〜🆒〜的事情。

First, you can chain constraints together so you won’t have to write multiple lines. This is super useful if you have a certain view’s attributes that have some sort of even relationship to another view’s single attribute —

首先,你可以约束在一起,这样你就不必写多行。 如果您拥有某个视图的属性与另一视图的单个属性具有某种偶数关系,则此功能非常有用-

make.width.height.equalTo(view.snp.width).dividedBy(2).offset(-30)

Here, both view1’s height and width attributes are fractionally related to the superview’s width by the same divisor and offset, so we can apply the appropriate constraint to both of them by chaining them together!

在这里, view1的height和width属性都通过相同的除数和偏移量与Superview的宽度部分相关,因此我们可以通过将它们链接在一起来对它们应用适当的约束!

If you ever want to pin a view’s edges to the superview’s, it’d be as simple as calling —

如果您想将视图的边缘固定到超级视图的边缘,则只需调用-

make.leading.trailing.top.bottom.equalToSuperview()

You can even call conveniences like edges to indicate you want to create constraints for all surrounding x and y-axis attributes for a view —

您甚至可以调用便利功能(如edges来表示您要为视图的所有周围x和y轴属性创建约束-

make.edges.equalToSuperview()

You can apply modifications like an offset, inset, or multiple by appending a method call at the end of your constraint declaration, which applies it to any attribute indicated on the left hand side (even chained ones) —

您可以通过在约束声明的末尾附加一个方法调用来应用诸如offsetinset或倍数之类的修改,并将其应用于左侧指示的任何属性(甚至是链式属性),

make.edges.equaltoSuperview().inset(
UIEdgeInsets(top: 5, left: 20, bottom: 5, right: 20)
)

And it goes without saying that translatesAutoresizingMaskIntoConstraints are already accounted for. 😌

不用说, translatesAutoresizingMaskIntoConstraints已经考虑在内。 😌

This is just a small subset of the functionality you get out of the box, and if the use case fits your app for those conveniences, I’d recommend giving it a try to see how it can clean up your UI!

这只是您开箱即用的功能的一小部分,如果用例适合您的应用程序以提供这些便利,我建议您尝试一下一下,看看它如何清理您的UI!

What I love about SnapKit specifically is that you can really get creative with your components. Custom programmatic UI can be a breeze to wire up, and the number of lines you’ll have to debug are a fraction of what an NSLayoutConstraint implementation would take.

我特别喜欢SnapKit,是您可以真正利用组件发挥创意。 自定义程序化UI可以很容易地连接起来,并且您必须调试的行数只是NSLayoutConstraint实现所用的一小部分。

We run the app and see the last example for our even squares 😢 —

我们运行该应用程序,然后查看偶数正方形的最后一个示例😢-

Image for post

Will you actually need half of the functionality you get from importing this? Probably not.

您实际上是否需要从导入此功能中获得一半的功能? 可能不是。

But why do people buy 1TB iPad Pros just to use Procreate? Why do we opt for new 13" Pros when our 15" MacBooks from a few years ago runs perfectly fine? Why do we get Hulu+ when we know we’re only going to watch maybe 3 channels from it?

但是,为什么人们仅仅为了使用Procreate而购买1TB iPad Pro? 当几年前我们的15英寸MacBook运行良好时,为什么我们选择新的13英寸Pro? 当我们知道只看3个频道时,为什么会收到Hulu +?

Because it’s cool.

因为很酷。

Because it’s fun to have more things.

因为拥有更多东西很有趣。

Because no one told me any better when I first started out. 💩

因为当我刚开始时没有人告诉我任何更好的方法。 💩

- ()

做出决定😨 (Making decisions 😨)

I love programmatic layout, whether first party or not. I love how concise it is with the right extensions. I love how you can line break to debug and don’t have to visually diagnose the storyboard when constraints aren’t pinning correctly and so on.

我喜欢程序化布局,无论是否是第一方。 我喜欢正确的扩展名多么简洁。 我喜欢如何换行进行调试,并且在约束未正确固定等情况下不必直观地诊断情节提要等。

  • “It’s easier to componentize UI.”

    “更容易将UI组件化。”

  • “It makes version control way more streamline.”

    “它使版本控制方式更加简化。”

  • “You get more control and customization out of your UI.”

    “您可以从UI中获得更多控制和自定义。”

And while there are grounds for these arguments, after getting the opportunity to dig a little deeper and explore IB, I found there are also plenty of benefits using the IB approach outside of bare code.

尽管有这些理由,但在有机会进行更深入的研究和探索IB之后,我发现在裸代码之外使用IB方法也有很多好处。

  • The volume of your interface code is seriously cut down.

    接口代码的数量已大大减少。

  • You can leverage XCode’s powerful built in auto layout functionality within Storyboard.

    您可以在Storyboard中利用XCode强大的内置自动布局功能。

  • You can literally see what you’re doing to your UI before running the app so runtime errors are significantly mitigated.

    在运行应用程序之前, 您可以 从字面上看到 您对UI所做的操作,从而大大减少了运行时错误。

The features I’m writing now, which I assumed would take a lot of wrestling in IB, have been pretty stress free to implement. I may still have a personal bias for programmatic layout, but for what it’s worth, even the Apple Documentation states:

我现在正在编写的功能(我认为在IB中将花费很多精力)已经非常容易实现。 对于程序布局,我可能仍然会有个人偏见,但就其价值而言,甚至Apple文档指出:

“Whenever possible, use Interface Builder to set your constraints. Interface Builder provides a wide range of tools to visualize, edit, manage, and debug your constraints.” — Tim Cook (probably)

“只要有可能,请使用Interface Builder设置约束。 Interface Builder提供了广泛的工具来可视化,编辑,管理和调试约束。” —蒂姆·库克(可能)

But their opinion doesn’t matter either. 😬

但是他们的意见也不重要。 😬

At the end of the day, the benefits of code-only or storyboard solutions are all moot when you don’t consider the impact to your codebase, team, or process.

归根结底,当您不考虑对代码库,团队或流程的影响时,纯代码解决方案或故事板解决方案的好处就无济于事了。

So to answer the question… It depends.

因此,回答这个问题…… 要视情况而定。

(I know, but you saw it coming a mile away) 🙃

(我知道,但是你看到了它一英里远)🙃

As a closing note, for you and me, we shouldn’t dismiss an approach due to predisposed notions without exploring how it’ll fit in our day-to-day, because there’s only one wrong way when it comes to implementing interfaces in Swift…

作为结束语,对您我来说,我们不应在没有探究它如何适应我们的日常情况的情况下就因习惯倾向而放弃这种方法,因为在Swift中实现接口只有一种错误的方法…

…frame-based layouts 🥴

…基于框架的布局🥴

Thanks for reading! I hope y’all are staying safe at this time. If you have any questions, don’t like the article, or just want to say hi — feel free to leave a comment! If you want to dig around, here’s a link to the code:

谢谢阅读! 希望大家现在都保持安全。 如果您有任何疑问,不喜欢这篇文章,或者只是想打个招呼-随时发表评论! 如果您想深入研究,请参见以下代码链接:

翻译自: https://uxdesign.cc/the-why-what-and-how-of-swift-auto-layout-9530a60eedf4

swift layout

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值