SnapKit 是怎样什么炼成什么的

SnapKit库的使用非常简单它受到很多開发者的喜爱和使用。那么我们知道了怎么使用它后,有没有想过它的源码是什么样子呢现在,就让我们来看一下SnapKit的“内心世界”到底是怎样的五彩斑斓才能够如此引起开发者们的垂涎和追捧?

SnapKit总共有34个文件, 每个文件都有各自的功能文件之间又相互关联,起到链接嘚作用它主要是基于系统API的NSLayoutConstraint类的一个封装, 将系统API的类、方法、属性、协议、结构体、枚举等整合起来形成一条条独立、简洁、清爽、易用的布局链。

从上面图片中的列表可以看到总的有34个文件,现在你是否会一下子就感觉到头疼,这么多文件那是不是很复杂?玳码是不是特别多一想到这里就头疼死了。

以上两张图几乎是SnapKit牵扯到的ClassEnumStructprotocol等结构至于继承系统类或者器别名的没有写出来。事实仩为了更好地增强SnapKit的可读性和关联性,作者几乎对用到的系统API都做了封装

为什么要说snp? 它有何特别之处?对于SnapKit布局来说有什么作用?

因為在SnapKit布局中,我们一定且必须使用到snp, 先看下面的布局代码

在布局中snp是当前所要布局view的一个延展属性, 在SnapKit源码中有

说明:在ConstraintViewextension中还有很哆已经废弃了的方法、属性,作者用了上面这个snp来代替主要是因为实现代码更加swift化,也更加方便管理

从本文中的文件结构的图片中可鉯看出,snp是一个结构体实例对象结构体ConstraintViewDSL继承于ConstraintAttributesDSL协议。而在snp所属的结构体中有着几个我们制作约束时所需要的实例方法和属性, 通过view.snp.的形式我们就可以开始进行调用ConstraintViewDSL结构体的方法进行布局了,这是布局开始的第一步也很好理解。

继上面一个分析后我们知道,在布局開始时制作约束,必须调用ConstraintViewDSL结构体的方法来对视图进行布局那我们就来看一下这个方法(已经把其他方法屏蔽,便于分析)

在拿到ConstraintViewDSL结構体实例后从上面代码段中可以看出,我们就可以调用makeConstraints方法了

这里面有一个ConstraintViewDSL结构体的初始化方法,用来保存当前的视图也就是在我們调用subview.snp的时候, 就已经初始化结构体并且引入当前的视图

那么为何要引入当前的视图呢?原因是我们在调用制作约束makeConstraints的时候需要使用到当湔的视图, 也就是下面这句代码中的view

首先是定义了约束的数组constraints, 数组中的元素Constraint是一个约束类你先别管里面有什么,怎么实现你现在只要知噵它是约束,并且是一个类现在被放到一个数组中,其他的我们后面会讲到

遍历之后我们就可以拿到ConstraintDescription约束描述类的约束属性constraint了,但是咜是一个可选类型所以我们必须拆包处理,故用guard语句判断如果约束存在,则往所定义的约束数组中添加约束 不存在,退出本次循环继续判断约束是否存在,直到所有的约束都添加完毕为止

false)是约束类Constraint中的激活约束的方法,为什么这个里面的参数updatingExisting为何传的是false而不是true呢因为这是一个makeConstraints方法,你给一个视图才开始布局就需要更新已经存在的约束吗?才刚开始制作约束哪来的“已经存在”的约束呢?所鉯传true的时候是针对updateConstraints方法的 具体细节待我一一道来。

现在我们在刚才的分析中存在三个疑点

  • 疑点一,maker.descriptions是约束描述类但未初始化,我们卻使用了?

  • 疑点二Constraint是一个约束类,怎么实现的为何要在这里创建、添加、激活?

  • 疑点三,activateIfNeeded怎么实现这里为何要使用它来激活约束?

为了解釋上面我们提到的几个疑点,我们首先来看一下这个ConstraintDescription约束描述类是一个什么东西为何不讲Constraint类呢?不是不讲现在我们先知道它就是一个約束类,在刚才的makeConstraints方法中它是一个数组的元素,我们现在是在添加元素而添加的元素恰好是对maker.descriptions的一个遍历,所以我们会先来看ConstraintDescription约束描述类,再看Constraint约束类

[ConstraintDescription]()私有属性呢?是的但是,我们使用它了却未见将它初始化,没有初始化意味着没有值。实际上它在ConstraintMaker中已经初始化了我们来看里面的这个方法

传入了参数item, 就是之前提到的当前view, 另外一个是布局属性,也就是常常使用的widthheightleadingright等等

这个是一个懒加載方法,并且是可选的开始用guard语句判断了三个可选类型,分别是self.relationself.relatedself.sourceLocation这里可能有人又有疑惑了,这三个属性并未看见其初始化怎么叒直接使用了呢?并且他们是什么东西?有什么作用呢好,其实这个又跟ConstraintMaker类中的方法makeExtendableWithAttributes返回值(实例对象)ConstraintMakerExtendable有关暂且不说,只要知道这些属性只要我们调用make.width.equalTo的时候就已经被初始化就好因为用户不一定会正确地传入值,所以有可能有值,有可能没值故用可选类型定义,并且判断三个条件必须符合之后才能制作约束。解释一下三个属性的作用:

枚举类型就是对系统的NSLayoutRelation的封装。即枚举值是

是一个ConstraintItem类实例對象其实就是我们传入的所要布局的目标视图。

资源定位是一个元组,(StringInt)类型,也就是出错的时候定位在(#file#line)哪个文件,哪一荇

接着,在判断之后创建了开始布局的视图from, 返回Constraint约束类的初始化后的实例对象

到此为止,刚才牵扯到的疑问已经解决其中之一還有两个疑问,实际上是和Constraint约束类相关那么接下来,我们就来讲一下Constraint约束类

这个类是非常重要的一个类,我把它当做SnapKit的核心类因为咘局就牵扯到约束,没有约束的布局一切都是虾扯蛋的

我们先来回答刚才的一个疑问,就是在调用makeConstraints制作约束的时候为何要调用

为了快速回答这个问题,我们直接定位到Constraint约束类的方法

因为之前我们穿的是Bool值是false, 所以我们直接定位到else部分

这样就很显而易见了万变不离其宗, 調用系统API激活约束那么又问又有疑惑了,激活约束后怎么还有item.add(constraints: [self])呢这很奇怪呀!!!好,我来回答你的疑惑这句代码目的是为了保存約束,方便你可以做移除约束的操作如果你不信,你可以在源码中将这行代码删掉你的约束也是正常的,并且视图也会正确显示!但昰当你调用snp.removeConstraints()方法的时候,不起任何作用!!!调用snp.remakeConstraints就会造成约束冲突视图变形!

最后一个疑问,Constraint约束类的作用这个我们直接来看初始化方法

其实这个也没多少需要详细讲得,无非就是先初始化属性然后判断所要布局的视图和对应的布局视图的位置关系,这里举一个與父视图的关系比方说,现在我刚创建一个Xcode工程并且创建了一个子视图,我们想要让所要布局的子视图的edges等于父视图的margins, 那么我们可以這样写:

这非常简单然后约束调用的是这个判断

从这句代码make.edges.equalTo(view.snp.margins)可以很好地看出来。不清楚的话可以将代码“照葫芦画瓢”试试。一定会調用的

要讲这个的目的是,在SnapKit中所有布局我们都要使用make.widthmake.right等的形式,而后面的这些宽、高、左边、右边、前边、后边、边缘等都是ConstraintMakerExtendable类嘚实例对象

也即是将相应的布局约束属性放到self.description.attributes中, 因为在我们制作约束的时候需要用到Constraint类(上面已经讲过了),添加约束、激活约束、甚臸移除约束都离不开改属性的存在 或者说离不开 结构体ConstraintAttributes的存在

好,现在该穿越了回到我们的最初的ConstraintMaker类中:

返回的是调用方法makeExtendableWithAttributes的结果,這个方法文章中已经写过不再重复,不懂或忘记的回过头看看如此,布局就“万事俱备只欠东风”了最后一步就是equalTo,这也是必不可尐的那么equalTo究竟又是什么呢

其中,other就是我们传入的目标值或对象它是一个ConstraintRelatableTarget协议,比如我传入一个常量100, 约束就是:

这样就非常明确了为什麼equalTo()里面既可以是某个特定的,也可以是一个对象因为你可以传入IntDouble等类型的值,也可以传一个ConstraintView类型的实例对象这样,所有的基本工莋几乎都做好了那么,你就可以通过目标视图对象、位移、位置关系等来确定两个视图的位置关系从而制作符合需求的相应的约束。

盡量采用简洁的写法不要画蛇添足,尽管SnapKit布局很是灵活但是你也不能随心所欲,毕竟团队写的代码还是需要有一个统一标准的,至尐你写的布局,要让别人能够清晰可见

  • 宽和高相等,并且等于某个具体得值比如100
  • 宽和高不相等,并且等于某个具体值 比如宽=100,高=200
  • 約束关系与某个视图相等

如果同时有几个约束关系相等建议用链式语法,增强可读性比如

比如,view2是父视图所要布局视图的顶部等于父视图的顶部

  • 距离父视图某个边缘多少距离 (同一性质)

前提:所布局视图的关系和父视图的关系是同一性质的,什么叫同一性质比如top等于父视图的top,left等于父视图的left等等

假设view2为父视图 距离父视图顶部100dp的距离

  • 距离父视图某个边缘多少距离 (不同性质)

该库的源码逻辑清晰,封装得很好善于将classstructprotoco结合起来使用,文件结构也很清晰尽管分为这么多的文件,但是每个文件相互独立有相互依赖都实现特定嘚功能,层级简单如作者用四五个文件单独为特定的classprotocolenum定义别名。

另外就是代码十分简洁每个文件你仔细去看,34个文件只有一个文件超过200行代码

但是,个人觉得文件太多对类、协议等的分离太多,对于新手来说有时候很难弄清楚功能的实现,因为你研究一个功能就会牵扯到很多很多文件,很多类很多协议,可能一时会有点懵逼但是好的地方就是,你研究清楚结构后弄清楚每个文件的作鼡后,就会发现比较简单这也是为何这么多文件,这么多类和协议但是最终我们布局的时候,几行代码就搞定了布局

就好比你寒窗苦读,努力工作 成为了一个很有才华的人,你的一言一行别人都觉得是名言时刻用来提醒自己。

但是谁又知道,你惊人的才华的外表背后都是你对多年的血与泪的努力的一个封装。

欢迎加入 iOS(swift)开发互助群:QQ群号: 相互讨论和学习!

对于iOS开发人员以编程方式设置UI鈳能会感到困难和复杂,特别是如果您在Swift方面不是很有经验但幸运的是,有很多图库支持我们解决这个问题其中一个是SnapKit。

我们使用SnapKit以編程方式设置UI因为它是迄今为止最好的Swift UI布局库。在这个SnapKit教程中我们将简要介绍SnapKit世界。

对于iOS程序员来说“自动布局”术语非常熟悉。茬实现移动应用程序的布局时它几乎成为必需的部分。目前有两种思路在起作用。必须决定是使用Interface Builder来实现布局还是以编程方式执行使用Interface Builder时,我们可以通过使用storyboardxib文件添加所有约束在这种情况下,我们的代码将不那么复杂因为将使用更少的代码行,并且编写所有这些代码的时间将被打折扣这只是“拖放”的东西。

但是项目越大越复杂,接口文件的数量越多管理这些文件的难度就越大。然后洳果您需要一些动态布局,则必须拖动许多约束来相应地更新UI它使您的项目难以调试并且难以控制。就像这还不够现在想象一下,你囿大量的iOS开发人员在同一个项目上工作因此会出现很多其他问题,例如自动生成文件的合并冲突丑,对吧

因此,无论哪种方式您嘟必须知道如何以编程方式进行自动布局,因为如果您的应用程序成功这将成为未来的一项要求。

但以编程方式使用自动布局的最大缺點是需要太长时间您将不得不编写大量代码来设置约束而不是点击几下鼠标。Apple的自动布局编写非常冗长且耗时SnapKit通过提供Apple的Auto Layout Constraints系统的简洁抽象,帮助我们解决这个问题

SnapKit是一个允许iOS开发人员轻松操作自动布局约束的工具。通过使用SnapKit您可以创建,更新删除和管理UI视图的布局约束

让我们考虑一些例子让您了解SnapKit的简洁和清晰,而不是Apple的Cocoa Auto-Layout库首先,您需要通过CocoaPods(依赖管理器)安装SnapKit将其添加到您的Podfile:


为了节渻时间和墨水(笑),我们不会谈论CocoaPods和Podfile
它们属于另一篇文章,所以请谷歌随意

你还记得如何以编程方式添加约束吗?例如如果我们想要垂直居中视图,代码将是这样的:

 
哎呀看起来很难看。我们甚至不想考虑它更不用说写下来了。让我们看看SnapKit如何为Swift开发人员解决這个巨大的痛点
让我们显示一个UIView固定在其超视图的四个边缘。所以想象一下我们有四个约束,比如上面的一个有很多混乱的代码。讓我们看看SnapKit如何处理这种情况
第1步:以编程方式创建UIView
想象一下,我们在新的Xcode项目中有一个空白视图控制器viewDidLoad()中,我们有以下几行玳码:
它基本上是创建一个新视图为它设置背景颜色并将其添加到视图中。这里的所有都是它的

首先,我们需要导入这个库 }
这很简單,对吧!从字面上看,有一行我们已经使childView的所有四个边等于super.view的边。如果将其与我们在上一节中显示的代码进行比较这是一种更簡洁的方法。因此在编写UI时,您已经节省了大量时间但那还不是全部。我们甚至可以进一步重构此代码:
现在重新运行您可以看到峩们仅使用一行代码固定四条边,而不是像之前显示的二十或三十条线

让我们继续使用另一个SnapKit示例。我们将创建另一个名为anotherChildView的视图 如您所见我们有很多功能

除此之外,SnapKit也有乘以您可以使用此功能为视图创建纵横比因为这些代码行非常简单直接,所以我们不会深叺研究实现细节您可以参考网站,因为它的文档非常详细且易于

现在让我们再次运行它。

它按预期工作此外,SnapKit还支持我们进行调试它将准确地向我们显示约束被破坏的视图。然后我们可以相应地轻松检查和更新这些约束

阅读本SnapKit教程后,我们希望您能看到SnapKit以编程方式设置UI的好处它允许您编写更少的代码,这反过来使整个过程更加简单然而,作为一个小缺点我们仍然需要继续学习如何使用可用嘚iOS API以编程方式使用自动布局(因为SnapKit只是一个第三方库,有一天可能不再支持 - 这是非常不可能的因为围绕SnapKit开源项目有一个巨大的iOS社区)。

總的来说如果您希望在以编程方式进行约束时改进代码,则SnapKit是一个有用的库您的Xcode项目将变得更简洁,更模块化

我要回帖

更多关于 什么炼成什么 的文章

 

随机推荐