放下故事板

每年在WWDC上,Apple都会大张旗鼓和鼓掌地宣传Interface Builder的新功能,通常是在顶级会议中,例如大型的“国情咨文”会议。 如果您在Interface Builder中布置视图,则是个好消息。 同时,如果您在代码中布置视图,则新功能很少而且相差很远。 iOS 9中引入的关于在代码中创建自动布局约束的新语法的唯一提及是在15分钟内被塞进了名为“自动布局之谜,第二部分”的会话中! 不完全是黄金时间。 真遗憾的是,Apple没有提倡更多地在代码中布局视图,并且没有以与更新Interface Builder相同的速度来改进这样做的工具。 在代码中放置视图可以真正提高生产力,并且可以很好地提高代码的整体质量。

为什么不只使用Interface Builder?

有许多原因应避免使用Interface Builder,其中一些原因与编写代码的优势有关,而某些原因与Storyboards和Interface Builder的缺点有关。 我将在这里讨论一些关键问题。 除非我特别提到情节提要,否则下面将仅使用术语“ 界面生成器” ,因为大多数问题都与所有情况有关。

首先,Interface Builder文件只能由Xcode生成和读取。 您可以将文件读取为XML,但是它们没有什么意义,或者至少,XML本身对事物的实际布局没有多大帮助。 当您发生合并冲突时,这会带来问题,对于包含许多视图控制器信息的情节提要,合并冲突会变得非常严峻。 您可以无意中更改视图或视图控制器的布局,这可以通过单击错误或打开文件来实现。 Interface Builder通常只是因为打开文件而对文件进行了更改。 当查看差异时,很难将其与您自己的更改区分开来,也很难进行推理。 换句话说,更改可能很难(在提交之前由您自己审核,而在进行代码审核时则由团队伙伴审核)。 由于Xcode幕后发生的所有变化都会使臭虫迷失,因此臭虫可能会被发现而未被发现。

Interface Builder的一个基本问题是,您在界面中看到的大部分内容都是视图的设计时快照。 您可以修饰视图以使其看起来像实时运行。 例如,从文件或服务器获取并仅在运行时加载的任何数据都必须在设计时进行模拟,并在运行时由实际数据替换,或者完全不在设计之列,这会破坏视觉布局的意义。 。 这将模拟数据与您实际想要显示在应用程序中的设计时数据混合在一起。 换句话说,您必须拆除设计时布局并在运行时构建运行时布局。 仅在设计应用程序时供您查看的数据很容易泄漏并成为运行时数据,这是非常不幸的。

您在Interface Builder中设置的某些属性只是更好地在代码中设置。 为了您自己,或者为了任何人继承您的代码。 颜色和字体是要在代码中以一种或另一种方式定义为常量的事物的绝佳示例。 如果它们将来会发生变化,我不会羡慕那些必须通过所有观点来选择新颜色或新字体的人。 如果您确实在代码中定义它们,则当前无法在Interface Builder中引用它们。 无论您做什么,都会有很多挑选的颜色和字体。 如果在Interface Builder中只保留设计中的字体和颜色,而仅在代码中进行设置,那么这将违反Interface Builder的目的。

代码对Interface Builder中对象的引用通常由字符串标识符组成,在这些字符串标识符上没有编译时检查。 如果您更改,删除或错误拼写了任何这些字符串,则可能会导致应用程序在运行时崩溃或停滞。 如果您没有在发布前测试确切的代码路径,那么您会感到不满意。

当您使用Interface Builder时,Swift由于强大的键入和编译时间检查而带给我们的许多代码安全性会丢失。 您的代码与Interface Builder之间存在很大的鸿沟,编译器不得不放弃,您必须信任自己(或团队成员)的Interface Builder技能。

插座仅在加载视图或“从笔尖唤醒”视图后才连接到视图控制器。 初始化后直到加载视图之前,在一定时间内您可以引用出口,但您无权访问它们。 如果您在Swift中将它们定义为非可选,则在加载视图之前尝试访问它们会崩溃。 碰巧,即使使用情节提要和脚本,也需要在代码中实例化视图控制器。 如果要在影响出口的初始化时间后在视图控制器上设置某些状态,则视图控制器将必须保持该状态,直到加载视图为止,或者必须在设置状态之前强制加载视图。 它可能导致状态重复,性能下降等。

用代码布局视图

我可以列出Interface Builder的许多其他缺点,但现在我将不遗余力,而是谈论其好处:在代码中布置视图!

当您在代码中管理整个视图生命周期时,可以使您的代码更简洁,更实用且更安全。 例如,在代码中设置UIButton意味着可以将在该按钮上设置的每个设置都放在一个位置。 如果您对按钮有疑问,就知道在哪里看! 您不必在情节提要中搜寻设置(或等待它甚至加载!),只需发现特定设置实际上是在代码中设置的,反之亦然。 实例化视图控制器时,您的按钮正在等待着您,这意味着您甚至可以在加载视图之前进行很多预检设置。 您根本不必关心它是否已加载。

您可以在视图控制器上创建自定义初始化程序,以定义视图控制器要使用的属性。 如果是WKWebView,则使用WKWebViewConfiguration对其进行初始化 然后, WKWebView的用户可以立即推断出此类依赖于其配置对象来工作,并且如果没有它,则不允许您初始化WKWebView 。 如果您有指定的初始化程序,则无法编译会出错的代码。 这称为依赖注入 ,它是一种出色的功能性工具,它根本不在Interface Builder布局的工具箱中,因为您必须使用Interface Builder的编码初始化程序。 对于在Interface Builder中定义的视图控制器,您必须检查在视图加载后是否在运行时正确设置了所有内容,如果不是,那么您无能为力。 依赖注入提高了可测试性,使您的代码更安全,更清晰,因为您可以声明哪些属性对类很重要,并在初始化期间管理期望。

在代码中,您可以使用扩展名或其他轻量级方法创建自定义视图而无需继承。 Interface Builder要求视图子类以可视方式表示自定义视图。 继承通常会引起问题,因为您必须推理上级或子类的内部实现,这会导致代码非常糟糕且难以调试。 同样,通过继承,您的代码用户需要关心MyCustomButton的功能,而不是仅仅处理普通的UIButton ,而唯一的区别是它的cornerRadius或背景图像。 苹果甚至警告不要在WWDC ’16的会话419中使用继承。 而是使用扩展程序自定义视图(fx按钮或其他控件),然后您可以立即在代码中设置视图。 您甚至可以在fx上进行巧妙的扩展。 UIButton或UITableView(如果您发现每次实例化它们时都对它们进行相似的配置,并且外观代理不足够)。

当您开始在代码中布置视图时,您还会发现可以以更加动态的方式进行操作,根据传入的数据构建视图层次结构,将布局任务推迟到有意义的特定时间甚至您可能开始思考关于如何以不同的方式组织视图层次结构,因为您不必将所有视图都一次定义在一个位置。 这就像添加一个全新的维度以查看布局。

当从代码转到Interface Builder时,您可能还会开始为上下文转换所困扰-从主要是基于键盘和文本的上下文切换到基于鼠标和控件的上下文。 等待Interface Builder加载,缩放,平移,击中微小的鼠标目标等。当您停留在代码中时,从模型到视图到控制器的切换很容易,您可以留在区域中

可以在中心位置配置约束,也可以在有意义的地方隔离较大的复杂视图来配置约束。 对于复杂的视图层次结构,您可能需要考虑将布局代码分成相关视图的大块,或者在将布局进行单独管理的情况下创建更多的自定义视图类。

代码约束听起来像一场噩梦!

首先,在代码中设置约束似乎是一项艰巨的任务,并且取决于所使用的语法,它也会让人感到乏味且重复。 仅苹果一个人就有至少三种声明代码约束的方式,以及无数的第三方选择,代码布局的方法似乎是无限的。 这既好又坏,但实际上主要是不好的,因为似乎没有人,而且至少在所有苹果公司中,都没有完善的自动布局语法。 不用绝望-一旦掌握了适合自己的语法,就可以希望有一种更声明性和简洁的方式来表示代码中的布局。

无论您喜欢使用哪种语法,我建议您至少熟悉Apple的一种语法选项,以便对Apple如何在代码中定义约束有基本的了解。 与Apple的API相比,我更喜欢使用某些第三方选项,因为我必须编写更少的样板代码,并且布局代码更易于理解和调试。 此外,大多数第三方框架都令人恐惧

  translationsAutoresizingMaskIntoConstraints = false 

在向视图添加约束时自动为您显示。 使用Apple的语法时,必须自己设置标志才能使布局正常工作,而我往往会忘记哪个令人沮丧。

对于第三方自动布局框架,我可以推荐SnapKit和制图。 前者是一种广泛使用的框架,您经常会在iOS和Mac上最常用的第三方框架的列表中找到这些框架,这些框架是从最初用Objective-C编写的旧Masonry框架移植而来的。 后者是一个较新的框架,在Swift中采用运算符重载来构建约束表达式。 我还没有做很多工作,但是看起来很有希望。

如果您对第三方布局框架不满意,但发现Apple的产品有点让人不知所措,请编写自己的扩展程序,以用于完成一些繁琐的工作fx。 UIView上的一种扩展,在添加约束或将给定视图的所有边缘捕捉到其父视图的所有边缘时,始终会设置translatesAutoresizingMaskIntoConstraints标志。有很多可能性可以通过一些简单的便捷功能来快速提高生产率。

代码视图并不适合所有人

娜塔莎机器人(Natasha The Robot)最近发布了这份对故事板的热爱宣言。 她提出了一些非常有效的观点,我将承认情节提要和Interface Builder当然也有自己的位置。 对于编码初学者,我什至建议您从Interface Builder开始,因为当您遇到很多其他新概念时,情节提要中的直观视图可以使您浮出水面,此外,您可能还希望Interface Builder提供视觉帮助来获得挂起的自动版式。

有些人认为,当他们从视觉上看事物时,他们会更好地理解事物,因此在代码中布局视图对他们不起作用。 可能是对的,但我认为对于许多人来说,这只是培训问题。 通常,您可以在Sketch或Photoshop中设计一个可以参考的设计,并且在学习设计时会很快学会在脑海中绘制约束。 一旦学习并理解了自动布局,您就可以编写出所需的代码行。

结论

在故事板上引用娜塔莎机器人:

Interface Builder是否有一些问题? 当然! 但是以我的经验,收益远远超过了问题。

对我来说,这是另一回事。 我一直在处理许多项目,有些使用Interface Builder,有些则没有。 基于Interface Builder的项目在很大程度上导致挫败感,并花费大量时间摆弄我本可以花在编码上的Interface Builder问题。 虽然,如果您要使用情节提要和笔尖,我同意Natasha的建议和最佳做法。

在苹果公司几乎无法识别代码中的“自动布局”的地方,我们处于一个怪异的地方,尤其是在开发者大会上。 当我与Apple工程师交谈时,有些人私下让我知道他们实际上也更喜欢代码的布局。 我经历过,当我告诉他们这件事时,有些人几乎被背叛了。 其他人则觉得这很有趣,而且有些让内在的人对Interface Builder的感觉和他们一样,这让他们松了一口气。

为什么一定要这样? 正如我已经提到的那样,我认识到Interface Builder是许多人喜欢的工具,许多人使用它来推断应用程序的外观或使用棘手的布局来帮助他们。 我也只是认为苹果应该提倡和改进代码选项,并且要开放和积极地进行代码开发,因为这样做的好处很多。 这里有很多要赢的地方。 我坚信,如果您在代码中进行布局,将会写出更好的应用程序。 您可以采用许多出色的实践,可以消除很多噪音。 放下情节提要,拾取代码。