關於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
基於職責對代碼進行分割,這樣會讓你的代碼變得更加優雅簡潔,會大大減小一些萬金油代碼的出現。減小閱讀代碼的成本也是咱們優化的一個方向,畢竟誰都不想由於混亂的代碼影響本身的心情