如何构建优雅的ViewController

栏目: IOS · 发布时间: 4年前

内容简介:关于ViewController讨论的最多的是它的肥胖和臃肿,即使使用传统的MVC模式,ViewController也可以写的很优雅,这无关乎设计模式,更多的是你对该模式理解有多深,你对于职责划分的认知是否足够清晰。ViewController也从很大程度上反应一个程序员的真实水平,初级程序员他的ViewController永远是臃肿的、肥胖的,什么功能都可以往里面塞,不同功能间缺乏清晰的界限。而一个优秀的程序员它的ViewController显得如此优雅,让你产生一种竟不能修改一笔一画的感觉。用户交互事件

关于ViewController讨论的最多的是它的肥胖和臃肿,即使使用传统的MVC模式,ViewController也可以写的很优雅,这无关乎设计模式,更多的是你对该模式理解有多深,你对于职责划分的认知是否足够清晰。ViewController也从很大程度上反应一个 程序员 的真实水平,初级程序员他的ViewController永远是臃肿的、肥胖的,什么功能都可以往里面塞,不同功能间缺乏清晰的界限。而一个优秀的程序员它的ViewController显得如此优雅,让你产生一种竟不能修改一笔一画的感觉。

ViewController职责

  • UI 属性配置 和 布局
  • 用户交互事件
  • 用户交互事件处理和回调

用户交互事件处理: 通常会交给其他对象去处理 回调 : 可以根据具体的 设计模式 和应用场景交给 ViewController 或者其他对象处理

而通常我们在阅读别人 ViewController 代码的时候,我们关注的是什么?

  1. 控件属性配置在哪里?
  2. 用户交互的入口位置在哪里?
  3. 用户交互会产生什么样的结果?(回调在哪里?)

所以从这个角度来说,这三个功能一开始就应该是被分离的,需要有清晰明确的界限。因为谁都不希望自己在查找交互入口的时候 ,去阅读一堆控件冗长的控件配置代码, 更不愿意在一堆代码中去慢慢理清整个用户交互的流程。 我们通常只关心我当前最关注的东西,当看到一堆无关的代码时,第一反应就是我想注释掉它。

基于协议分离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()
    }
}


复制代码

这里 我将控件的添加和控件的配置分成两个函数 addSubViewsconfigureSubViewsProperty , 因为在我的眼里函数就应该遵循单一职责这个概念: 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, 尽量收集在同一个区域中, 同时对于每个回调加上必要的注释:

  • 由哪种操作触发
  • 会导致什么后果
  • 最终会留下哪里

所以从这个角度来说 UITableViewDataSourceUITableViewDelegate 完全是两种不一样的行为, 一个是 configure UI , 一个是 control behavior , 所以不要在把这两个东西写一块了, 真的很难看。

总结

基于职责对代码进行分割,这样会让你的代码变得更加优雅简洁,会大大减少一些万金油代码的出现,减少阅读代码的成本也是我们优化的一个方向,比较谁都不想因为混乱的代码影响自己的心情


以上所述就是小编给大家介绍的《如何构建优雅的ViewController》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web全栈工程师的自我修养

Web全栈工程师的自我修养

余果 / 人民邮电出版社 / 2015-9-1 / 49.00

全栈工程师正成为 IT 行业的新秀,论是上市互联网公司还是创业公司,都对全栈工程师青睐有加。本书作者是腾讯公司高级工程师,在前端、后端和APP开发方面都有丰富的经验,在本书中分享了全栈工程师的技能要求、核心竞争力、未来发展方向、对移动端的思考。除此之外,本书还详细记录了作者从零开始、学习成长的心路历程。 本书内容全面,客观务实,适合互联网行业新人、程序员,以及期待技术转型的从业者阅读参考。一起来看看 《Web全栈工程师的自我修养》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

html转js在线工具
html转js在线工具

html转js在线工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具