關於ViewController討論的最多的是它的肥胖和臃腫,即便使用傳統的MVC模式,ViewController也能夠寫的很優雅,這無關乎設計模式,更多的是你對該模式理解有多深,你對於職責劃分的認知是否足夠清晰。ViewController也從很大程度上反應一個程序員的真實水平,初級程序員他的ViewController永遠是臃腫的、肥胖的,什麼功能均可以往裏面塞,不一樣功能間缺少清晰的界限。而一個優秀的程序員它的ViewController顯得如此優雅,讓你產生一種竟不能修改一筆一畫的感受。程序員
用戶交互事件處理: 一般會交給其餘對象去處理 回調: 能夠根據具體的設計模式和應用場景交給 ViewController 或者其餘對象處理設計模式
而一般咱們在閱讀別人ViewController
代碼的時候,咱們關注的是什麼?markdown
因此從這個角度來講,這三個功能一開始就應該是被分離的,須要有清晰明確的界限。由於誰都不但願本身在查找交互入口的時候 ,去閱讀一堆控件冗長的控件配置代碼, 更不肯意在一堆代碼中去慢慢理清整個用戶交互的流程。 咱們一般只關心我當前最關注的東西,當看到一堆無關的代碼時,第一反應就是我想註釋掉它。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
: 明確告訴閱讀者,控件的全部屬性配置都在這裏,想要修改屬性請閱讀這個函數佈局
來看一個實例:優化
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可能就是這樣ui
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, 儘可能收集在同一個區域中, 同時對於每一個回調加上必要的註釋:spa
因此從這個角度來講UITableViewDataSource
和 UITableViewDelegate
徹底是兩種不同的行爲, 一個是 configure UI , 一個是 control behavior , 因此不要在把這兩個東西寫一塊了, 真的很難看。設計
基於職責對代碼進行分割,這樣會讓你的代碼變得更加優雅簡潔,會大大減小一些萬金油代碼的出現。減小閱讀代碼的成本也是咱們優化的一個方向,畢竟誰都不想由於混亂的代碼影響本身的心情