内容简介:关于ViewController讨论的最多的是它的肥胖和臃肿,即使使用传统的MVC模式,ViewController也可以写的很优雅,这无关乎设计模式,更多的是你对该模式理解有多深,你对于职责划分的认知是否足够清晰。ViewController也从很大程度上反应一个程序员的真实水平,初级程序员他的ViewController永远是臃肿的、肥胖的,什么功能都可以往里面塞,不同功能间缺乏清晰的界限。而一个优秀的程序员它的ViewController显得如此优雅,让你产生一种竟不能修改一笔一画的感觉。用户交互事件
关于ViewController讨论的最多的是它的肥胖和臃肿,即使使用传统的MVC模式,ViewController也可以写的很优雅,这无关乎设计模式,更多的是你对该模式理解有多深,你对于职责划分的认知是否足够清晰。ViewController也从很大程度上反应一个 程序员 的真实水平,初级程序员他的ViewController永远是臃肿的、肥胖的,什么功能都可以往里面塞,不同功能间缺乏清晰的界限。而一个优秀的程序员它的ViewController显得如此优雅,让你产生一种竟不能修改一笔一画的感觉。
ViewController职责
- UI 属性配置 和 布局
- 用户交互事件
- 用户交互事件处理和回调
用户交互事件处理: 通常会交给其他对象去处理 回调 : 可以根据具体的 设计模式 和应用场景交给 ViewController 或者其他对象处理
而通常我们在阅读别人 ViewController
代码的时候,我们关注的是什么?
- 控件属性配置在哪里?
- 用户交互的入口位置在哪里?
- 用户交互会产生什么样的结果?(回调在哪里?)
所以从这个角度来说,这三个功能一开始就应该是被分离的,需要有清晰明确的界限。因为谁都不希望自己在查找交互入口的时候 ,去阅读一堆控件冗长的控件配置代码, 更不愿意在一堆代码中去慢慢理清整个用户交互的流程。 我们通常只关心我当前最关注的东西,当看到一堆无关的代码时,第一反应就是我想注释掉它。
基于协议分离UI属性的配置
protocol MFViewConfigurer { var rootView: UIView { get } var contentViews: [UIView] { get } var contentViewsSettings: [() -> Void] { get } func addSubViews() func configureSubViewsProperty() func configureSubViewsLayouts() func initUI() } 复制代码
依赖这个协议就可以完成所有控件属性配置,然后通过extension protocol 大大减少重复代码,同时提高可读性
extension MFViewConfigurer { func addSubViews() { for element in contentViews { if let rootView = rootView as? UIStackView { rootView.addArrangedSubview(element) } else { rootView.addSubview(element) } } } func configureSubViewsProperty() { for element in contentViewsSettings { element() } } func configureSubViewsLayouts() { } func initUI() { addSubViews() configureSubViewsProperty() configureSubViewsLayouts() } } 复制代码
这里 我将控件的添加和控件的配置分成两个函数 addSubViews
和 configureSubViewsProperty
, 因为在我的眼里函数就应该遵循单一职责这个概念: addSubViews
: 明确告诉阅读者,我这个控制器包含哪些控件 configureSubViewsProperty
: 明确告诉阅读者,控件的所有属性配置都在这里,想要修改属性请阅读这个函数
来看一个实例:
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // 初始化 UI initUI() // 绑定用户交互事件 bindEvent() // 将ViewModel.value 绑定至控件 bindValueToUI() } // MARK: - UI configure // MARK: - UI extension MFWeatherViewController: MFViewConfigurer { var contentViews: [UIView] { return [scrollView, cancelButton] } var contentViewsSettings: [() -> Void] { return [{ self.view.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.7) self.scrollView.hiddenSubViews(isHidden: false) }] } func configureSubViewsLayouts() { cancelButton.snp.makeConstraints { make in if #available(iOS 11, *) { make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top) } else { make.top.equalTo(self.view.snp.top).offset(20) } make.left.equalTo(self.view).offset(20) make.height.width.equalTo(30) } scrollView.snp.makeConstraints { make in make.top.bottom.left.right.equalTo(self.view) } } } 而对于UIView 这套协议同样适用 ```Swift // MFWeatherSummaryView private override init(frame: CGRect) { super.init(frame: frame) initUI() } // MARK: - UI extension MFWeatherSummaryView: MFViewConfigurer { var rootView: UIView { return self } var contentViews: [UIView] { return [ cityLabel, weatherSummaryLabel, temperatureLabel, weatherSummaryImageView, ] } var contentViewsSettings: [() -> Void] { return [UIConfigure] } private func UIConfigure() { backgroundColor = UIColor.clear } public func configureSubViewsLayouts() { cityLabel.snp.makeConstraints { make in make.top.centerX.equalTo(self) make.bottom.equalTo(temperatureLabel.snp.top).offset(-10) } temperatureLabel.snp.makeConstraints { make in make.top.equalTo(cityLabel.snp.bottom).offset(10) make.right.equalTo(self.snp.centerX).offset(0) make.bottom.equalTo(self) } weatherSummaryImageView.snp.makeConstraints { make in make.left.equalTo(self.snp.centerX).offset(20) make.bottom.equalTo(temperatureLabel.snp.lastBaseline) make.top.equalTo(weatherSummaryLabel.snp.bottom).offset(5) make.height.equalTo(weatherSummaryImageView.snp.width).multipliedBy(61.0 / 69.0) } weatherSummaryLabel.snp.makeConstraints { make in make.top.equalTo(temperatureLabel).offset(20) make.centerX.equalTo(weatherSummaryImageView) make.bottom.equalTo(weatherSummaryImageView.snp.top).offset(-5) } } } 复制代码
由于我使用的是MVVM模式,所以 viewDidLoad
和MVC模式还是有些区别,如果是MVC可能就是这样
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // 初始化 UI initUI() // 用户交互事件入口 addEvents() } // MARK: callBack ...... 复制代码
由于MVC的回调模式很难统一,有Delegate, closure, notification 等等,所以回调通常会散落在控制器各个角落。最好加个 MARK
flag, 尽量收集在同一个区域中, 同时对于每个回调加上必要的注释:
- 由哪种操作触发
- 会导致什么后果
- 最终会留下哪里
所以从这个角度来说 UITableViewDataSource
和 UITableViewDelegate
完全是两种不一样的行为, 一个是 configure UI , 一个是 control behavior , 所以不要在把这两个东西写一块了, 真的很难看。
总结
基于职责对代码进行分割,这样会让你的代码变得更加优雅简洁,会大大减少一些万金油代码的出现,减少阅读代码的成本也是我们优化的一个方向,比较谁都不想因为混乱的代码影响自己的心情
以上所述就是小编给大家介绍的《如何构建优雅的ViewController》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- vueSSR: 从0到1构建vueSSR项目 --- 路由的构建
- 在 Android Studio 里使用构建分析器提升构建性能
- [译] 使用 React 和 ImmutableJS 构建一个拖放布局构建器
- 为 Envoy 构建控制面指南第4部分:构建的可扩展性
- 自动化构建工具 Gradle 4.5 RC1 发布,改进构建缓存
- 构建工具篇 - react 的 yarn eject 构建命令都做了什么
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
光线跟踪算法技术
萨芬 / 刘天慧 / 清华大学出版社 / 2011-3 / 98.00元
《光线跟踪算法技术》详细阐述了与光线跟踪问题相关的高效解决方案及相应的数据结构和算法,主要包括采样技术、投影视图、视见系统、景深、非线性投影、立体视觉、光照与材质、镜面反射、光泽反射、全局光照、透明度、阴影、环境遮挡、区域光照、光线与对象间的相交计算、对象变换、栅格技术以及纹理映射技术等内容。此外,《光线跟踪算法技术》还提供了相应的算法、代码以及伪代码,以帮助读者进一步理解计算方案的实现过程。 ......一起来看看 《光线跟踪算法技术》 这本书的介绍吧!