日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何构建优雅的ViewController

發布時間:2025/7/14 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何构建优雅的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.// 初始化 UIinitUI()// 綁定用戶交互事件bindEvent()// 將ViewModel.value 綁定至控件bindValueToUI()}// MARK: - UI configure// MARK: - UIextension 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 inif #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 inmake.top.bottom.left.right.equalTo(self.view)}}}而對于UIView 這套協議同樣適用```Swift // MFWeatherSummaryViewprivate override init(frame: CGRect) {super.init(frame: frame)initUI()}// MARK: - UIextension 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 inmake.top.centerX.equalTo(self)make.bottom.equalTo(temperatureLabel.snp.top).offset(-10)}temperatureLabel.snp.makeConstraints { make inmake.top.equalTo(cityLabel.snp.bottom).offset(10)make.right.equalTo(self.snp.centerX).offset(0)make.bottom.equalTo(self)}weatherSummaryImageView.snp.makeConstraints { make inmake.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 inmake.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.// 初始化 UIinitUI()// 用戶交互事件入口addEvents()}// MARK: callBack......復制代碼

    由于MVC的回調模式很難統一,有Delegate, Closure, Notification、KVC等,所以回調通常會散落在控制器各個角落。最好加個MARK flag, 盡量收集在同一個區域中, 同時對于每個回調加上必要的注釋:

    • 由哪種操作觸發
    • 會導致什么后果
    • 最終會通往哪里

    所以從這個角度來說UITableViewDataSource 和 UITableViewDelegate 完全是兩種不一樣的行為, 一個是 configure UI , 一個是 control behavior , 所以不要在把這兩個東西寫一塊了, 真的很難看。

    總結

    基于職責對代碼進行分割,這樣會讓你的代碼變得更加優雅簡潔,會大大減少一些萬金油代碼的出現。減少閱讀代碼的成本也是我們優化的一個方向,畢竟誰都不想因為混亂的代碼影響自己的心情

    總結

    以上是生活随笔為你收集整理的如何构建优雅的ViewController的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。