• 原文地址:Writing Cleaner View Code in Swift By Overriding loadView()
  • 原文作者:Bruno Rocha
  • 译文出自:掘金翻译计划
  • 本文永久链接:github.com/xitu/gold-m…
  • 译者:RickeyBoy
  • 校对者:徐键

究竟选择使用 Storyboards 还是纯代码书写 view 是非常主观的事情。在对两种方式都进行了尝试之后,我个人支持使用纯代码书写 view 来完成项目,这样能够允许多人编辑相同的类而不产生讨厌的冲突,也更方便进行代码审查。

在最开始练习纯代码写 view 的时候,人们普遍遇到的一个问题是最开始不知道将代码放在哪里。如果你采用普通 storyboard 的方式,将所有相关代码都放进你的 ViewController 之中,这样很容易会最终产生一个巨大的上帝类:

final class MyViewController: UIViewController {private let myButton: UIButton = {//}()private let myView: UIView = {//}()// 其他 10 个左右的 viewoverride func viewDidLoad() {super.viewDidLoad()setupViews()}private func setupViews() {setupMyButton()setupMyView()// 设置其他的 view}private func setupMyButton() {view.addSubview(myButton)// 十行约束代码}private func setupMyView() {view.addSubview(myView)// 十行约束代码}// 所有其他的设置// 所有 ViewModel 的逻辑// 所有 Button 的点击逻辑等东西...
}
复制代码

你可以通过把 view 移动到不同的文件并添加引用到原来的 ViewController 之中来改善这样的情况,但是你仍然需要用本不应该在 ViewController 中的内容填满 ViewController,就比如约束代码和其他设置 view 的代码 — 更不用说你现在有两个 view 属性(myView 和原生 view)在 ViewController 之中,而这没有任何好处。

final class MyViewController: UIViewController {let myView = MyView()override func viewDidLoad() {super.viewDidLoad()setupMyView()}private func setupMyView() {view.addSubview(myView)// 10 行左右的约束代码myView.delegate = self// 现在我们同时有了 view 和 MyView...}
}
复制代码

臃肿的 ViewController 以及逻辑过多的 ViewController 都非常难以管理和维护。在像 MVVM 这样的架构下,ViewController 应该主要作为自身的 View 以及 ViewModel 之间的路由器 -- 设置并且约束 View 并不是它们的职责,ViewController 只应该起到前后传递信息的路由作用

在一个大部分代码都是关于自身 View 的视图代码项目中,能够清晰地拆分你的架构中各部分的职责,对于一个便于维护的项目来说非常重要。你要让你真正构建视图部分的代码完全和你的 ViewController 分离 -- 幸运的是有一个简单的方法,就是重写 UIViewController 中原生的 View 属性。这样做允许你在分离的文件中管理你的多个 View,同时也仍能保证你的 ViewController 不用去设置任何 View。

loadView()

loadView()UIViewController 中并不常见的一个方法,但它是 ViewController 的生命周期中非常重要的一部分,因为它承担着最开始加载出 view 属性的责任。当使用 Storyboard 的时候,它会加载出 nib 并将其附加给 view,但当手动初始化 ViewController 时,这个方法所做的一切就是创建出一个空的 UIView。你可以重写这个方法并改变它的行为,并且在 ViewController 的 view 上添加任何类型的 view。

final class MyViewController: UIViewController {override func loadView() {let myView = MyView()myView.delegate = selfview = myView}override func viewDidLoad() {super.viewDidLoad()print(view) // 一个 MyView 的实例}
}
复制代码

注意 view 会自动的约束自己到 ViewController 的边界,所以并不需要为 myView 设置外部约束!

现在,view 成为了我自定义的 view(在本例中为 MyView)的一个引用。你可以在这个 view 独立的文件内部构建其所有功能,并且 ViewController 对此毫无权限。太棒了!

为了获取 MyView 中的内容,你可以将 View 强制转换为你自己的类型:

var myView: MyView {return view as! MyView
}
复制代码

这样看起来有点奇怪,但这是因为 view 将仍然被定义为 UIView 类型,而不是你为它定义的类型。

为了避免我的 ViewController 中重复出现这样的代码,我喜欢创建一个 CustomView 协议,并在其中定义包含关联类型的行为:

/// HasCustomView 协议为 UIViewController 定义了一个 customView 属性,它是为了去代替普通的 view 属性。
/// 为了实现这些,你必须在 loadView() 方法时为你的 UIViewController 提供一个自定义的 View。
public protocol HasCustomView {associatedtype CustomView: UIView
}extension HasCustomView where Self: UIViewController {/// UIViewController 的自定义 view。public var customView: CustomView {guard let customView = view as? CustomView else {fatalError("Expected view to be of type \(CustomView.self) but got \(type(of: view)) instead")}return customView}
}
复制代码

最终会:

final class MyViewController: UIViewController, HasCustomView {typealias CustomView = MyViewoverride func loadView() {let customView = CustomView()customView.delegate = selfview = customView}override func viewDidLoad() {super.viewDidLoad()customView.render() // 一些 MyView 的方法}
}
复制代码

如果每次都定义这个 CustomView 类型别名会让你有点烦,那么你可以进一步在泛型类中定义这些行为:

class CustomViewController<CustomView: UIView>: UIViewController {var customView: CustomView {return view as! CustomView // 因为我们正在重写 view,所以永远不会解析失败。}override func loadView() {view = CustomView()}
}final class MyViewController: CustomViewController<MyView> {override func loadView() {super.loadView()customView.delegate = self}
}
复制代码

我个人不太喜欢泛型的方式,因为编译器并不允许泛型类具有的 @objc 方法的扩展,这会禁止你在扩展中拥有 UITableViewDataSource 之类的协议。但是,除非你需要做一些特殊的事情(比如设置委托),它会允许你跳过重写 loadView() 这一步,从而能保持 ViewController 的整洁。

结论

重写 loadView() 是一个让你的视图代码项目更加易于理解、易于维护的好方法,并且我已经使用 HasCustomView 方法获得了非常良好的效果,特别是在最近几个项目中。编写视图部分的代码也许不是你的选择,但是它带来了很多显而易见的好处。尝试一下吧,看看它是不是更适合你。

如果你有更好的定义 view 并且不需要 storyboard 的方法,或者你可能有一些疑问、意见或者反馈,请让我知道。

参考文献和推荐阅读

苹果官方文档:loadView()

如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。

[译] 重写 loadView() 方法使 Swift 视图代码更加简洁相关推荐

  1. ES6技巧和窍门,使您的代码更简洁,更短且更易于阅读!

    by Sam Williams 通过山姆·威廉姆斯 ES6技巧和窍门,使您的代码更简洁,更短且更易于阅读! (ES6 tips and tricks to make your code cleaner ...

  2. android studio提示要重写的方法,Android Studio 重写方法时参数命名异常

    kuangbin_SegTree M (HDU 4553) put my gezi这句话不得不说我看了好几秒才反应过来什么意思(你咋不上天呢 目测了一下也是区间合并 但是是成段更新的区间合并 但是!我 ...

  3. 关于子类重写父类方法的问题

    关于访问修饰符对重写父类方法的影响 结论 代码样例 子类 结论 子类重写父类的方法: 1.父类中私有方法不能被重写.因为父类私有方法子类可理解为被子类继承,但是不被子类可见,不可访问,当然就无法重写了 ...

  4. 使C#代码现代化——第二部分:方法

    目录 介绍 背景 什么是方法? 现代方式 扩展方法 委托 Lambda表达式 LINQ表达式 方法表达式 局部函数 结论 介绍 近年来,C#已经从一种具有一个功能的语言发展成为一种语言,其中包含针对单 ...

  5. [转载] JAVA面向对象之代码块 继承 方法的重写 super关键字与重写toString()方法介绍

    参考链接: 可以重写Java中的私有方法吗 JAVA面向对象之代码块与继承 代码块分类 局部代码块 作用:限制变量生命周期 书写位置:在方法中 构造代码块 开发中很少使用 书写位置:类中  方法外 调 ...

  6. swift视图容器_如何使用IBDesignable在Swift中创建漂亮的,可重复使用的渐变视图...

    swift视图容器 by Lee Dowthwaite 通过李道思韦特 如何使用IBDesignable在Swift中创建漂亮的,可重复使用的渐变视图 (How to create a beautif ...

  7. 【译】最大限度地降低多线程 C# 代码的复杂性

    [译]最大限度地降低多线程 C# 代码的复杂性 原文:[译]最大限度地降低多线程 C# 代码的复杂性 分支或多线程编程是编程时最难最对的事情之一.这是由于它们的并行性质所致,即要求采用与使用单线程的线 ...

  8. 【苹果推iMessage送】摆设overrideUserInterfaceStyle属性以使该视图及其子视图具备特定的UIUserInterfaceStyle

    推荐内容IMESSGAE相关 作者推荐内容 iMessage苹果推软件 *** 点击即可查看作者要求内容信息 作者推荐内容 1.家庭推内容 *** 点击即可查看作者要求内容信息 作者推荐内容 2.相册 ...

  9. java 重写equals方法的种种“坑”

    重写java object类的equals方法 覆盖equals方法请遵守约定 什么情况下要覆盖equals方法 容易违反的对称性 不易察觉的传递性 覆盖equals请遵守通用约定 似乎覆盖equal ...

最新文章

  1. 蓝桥杯 扑克序列(全排列)
  2. XenServer中LVM的性能
  3. 《剑指offer》合并两个排序的链表
  4. [Usaco2015 dec]Breed Counting
  5. 吴恩达机器学习训练1:线性回归(多变量)
  6. 为什么有人说开车时,最能看出一个人的情商和潜力?
  7. 我们决定聚在一起搞大事
  8. Lucene: 全文检索的基本原理
  9. android excel加图片,安卓和iOS版的Excel现在可以拍摄图片上的表格转为电子表格
  10. [软件笔试] 2014暴风影音校招技术笔试题(长春站)
  11. 免费开源的拉曼光谱分析软件 Raman Spectral Analysis software
  12. 覆盖率验证——代码覆盖率+功能覆盖率
  13. python plt绘制柱状图形+柱状图增加数字标注
  14. 南京航空大学c语言课程设计,南京航空航天大学C语言课程设计报告.doc
  15. OpenCv--提取水平和垂直线(通过膨胀和腐蚀操作)
  16. 这个Python游戏库,打开就能玩一天!
  17. epson连接计算机后无法打印,EPSON针式打印机连接正常无法打印解决
  18. 2. Hadoop的安装(这你都没装好,我就服了)
  19. shell真假01的真相
  20. vue ts项目同时引入ant-design和element-ui,ts报错不能编译打包失败

热门文章

  1. python安装不了jieba_python安装jieba失败怎么办?_后端开发
  2. 第八届国际BCI会议(2021 Virtual BCI meeting)
  3. 一切都是问题,一切都着落在自身
  4. 亚信科技CTO欧阳晔博士:5G网络助推边缘AI|MEET 2022
  5. 命令行里给个注释,AI就能自动生成代码
  6. 往年包场丘赛的北大,今年被清华逆袭了
  7. 美团北京,今日起无人驾驶送外卖
  8. 向碳基芯片更进一步:台积电、斯坦福等联手开发碳纳米管晶体管新工艺,性能逼近硅元件...
  9. 周末,我打开《逆水寒》参加了一场AI学术会议
  10. 史上最全SpaceX火箭数据开源,核心、组员舱、起落架、发射信息全都有!