关于ViewController讨论的最多的是它的肥胖和臃肿,即便使用传统的MVC模式,ViewController也能够写的很优雅,这无关乎设计模式,更多的是你对该模式理解有多深,你对于职责划分的认知是否足够清晰。ViewController也从很大程度上反应一个程序员的真实水平,初级程序员他的ViewController永远是臃肿的、肥胖的,什么功能均可以往里面塞,不一样功能间缺少清晰的界限。而一个优秀的程序员它的ViewController显得如此优雅,让你产生一种竟不能修改一笔一画的感受。程序员
用户交互事件处理: 一般会交给其余对象去处理 回调: 能够根据具体的设计模式和应用场景交给 ViewController 或者其余对象处理设计模式
而一般咱们在阅读别人ViewController
代码的时候,咱们关注的是什么?ide
因此从这个角度来讲,这三个功能一开始就应该是被分离的,须要有清晰明确的界限。由于谁都不但愿本身在查找交互入口的时候 ,去阅读一堆控件冗长的控件配置代码, 更不肯意在一堆代码中去慢慢理清整个用户交互的流程。 咱们一般只关心我当前最关注的东西,当看到一堆无关的代码时,第一反应就是我想注释掉它。函数
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
: 明确告诉阅读者,控件的全部属性配置都在这里,想要修改属性请阅读这个函数优化
来看一个实例:ui
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可能就是这样spa
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 初始化 UI
initUI()
// 用户交互事件入口
addEvents()
}
// MARK: callBack
......
复制代码
因为MVC的回调模式很难统一,有Delegate, Closure, Notification、KVC等,因此回调一般会散落在控制器各个角落。最好加个MARK
flag, 尽可能收集在同一个区域中, 同时对于每一个回调加上必要的注释:设计
因此从这个角度来讲UITableViewDataSource
和 UITableViewDelegate
彻底是两种不同的行为, 一个是 configure UI , 一个是 control behavior , 因此不要在把这两个东西写一块了, 真的很难看。code
基于职责对代码进行分割,这样会让你的代码变得更加优雅简洁,会大大减小一些万金油代码的出现。减小阅读代码的成本也是咱们优化的一个方向,毕竟谁都不想由于混乱的代码影响本身的心情