走進MVVM

MVVM與MVC有什麼不一樣

1.多了一個"視圖模型"類前端

2.視圖控制器再也不對模型具備訪問權限,而是經過新的ViewModel交互bash

3.爲了解決複雜的View/ViewContoller,以便於單獨的訪問和測試,符合模塊化開發的思想架構

帶來哪些變化

MVVM是一個前端架構,它專一於將用戶界面的開發與業務邏輯的開發工做分離app

咱們專一於使用更好的架構提升代碼質量,提升代碼的可測試性,讓架構設計清晰合理。市面上有那麼多架構,咱們沒法一一的去徹底掌握每個架構的操做,可是咱們能夠記住一個簡單的規則:ide

no matter what architecture we decide to use, the ultimate goal is to make test simpler.模塊化

MVVM由John Gossman於2005年提出.MVVM的主要目的是將數據狀態從View移動到ViewModel。佈局

根據定義,View僅包含視覺元素,咱們只執行佈局,動畫,初始化UI組件等操做。在View和Model之間有一個稱爲ViewModel的特殊層。ViewModel提供了一組接口,每一個接口表明View中的UI組件。咱們使用一種稱爲「綁定」的技術將UI組件鏈接到ViewModel接口。所以,在MVVM中,咱們不直接觸摸View,咱們在ViewModel中處理業務邏輯,所以View會相應地更改自身。
測試


如上圖。一般,ViewModel從View接收用戶交互,從Model中獲取數據,而後將數據處理爲一組準備顯示的屬性。觀察ViewModel的更改後,View會自動更新。這就是MVVM的整個故事。
動畫

具體來講,對於iOS開發中的MVVM,UIView / UIViewController表明View。(這裏去理解MVVM就是從視圖層去弱化數據邏輯,分理出一個ViewModel接口層去處理複雜邏輯,View只專一於UI內容和數據綁定)咱們只作:ui

  1. 啓動/佈局/呈現UI組件。

  2. 使用ViewModel綁定UI組件。

另外一方面,在ViewModel中,咱們作:

  1. 編寫控制器邏輯,例如分頁,錯誤處理等。

  2. 編寫表示邏輯,爲View提供接口。

實現數據綁定

使用代理方法實現View和ViewModel的數據雙向綁定

ViewController

import UIKit
class ViewController: UIViewController {
    
    var viewModel: ViewModel!
    @IBOutlet weak var userField: UITextField!
    @IBOutlet weak var passField: UITextField!
    @IBOutlet weak var repestField: UITextField!
    @IBOutlet weak var userLab: UILabel!
    @IBOutlet weak var passLab: UILabel!
    @IBOutlet weak var repestLab: UILabel!
    @IBOutlet weak var loginBtn: UIButton!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        userField.addTarget(self, action: #selector(userObserver(_:)), for: .editingChanged)
        passField.addTarget(self, action: #selector(passObserver(_:)), for: .editingChanged)
        repestField.addTarget(self, action: #selector(repeatObnserver(_:)), for: .editingChanged)
        viewModel = ViewModel()
        viewModel?.delegate = self
        reloadViews()
    }

    // 須要操做,以後經過傳入的數據進行數據操做
    @objc func userObserver(_ tx: UITextField) {
        viewModel?.validatedUsername(user: tx.text ?? "")
    }
    @objc func passObserver(_ tx: UITextField) {
        viewModel?.validatedPassword(pass: tx.text ?? "")
    }
    @objc func repeatObnserver(_ tx: UITextField) {
        viewModel?.validatedPasswordRepeated(repeatpass: tx.text ?? "")
    }
    @IBAction func goButton(_ sender: UIButton) {
        viewModel?.login()
    }
}

//MARK: ViewModelDelegate
extension ViewController: ViewModelDelegate {
    //雙向綁定用於刷新
    func reloadViews() {
        userLab.backgroundColor = viewModel.userColor
        passLab.backgroundColor = viewModel.passColor
        repestLab.backgroundColor = viewModel.repeatpassColor
        loginBtn.isEnabled = viewModel.loginButtonEnable
        loginBtn.setTitleColor(viewModel.loginButtonBack, for: .normal)
        loginBtn.backgroundColor = viewModel.loginButtontextColor
    }
    func alertInfo() {
        
    }
    func moveToHomeScreen() {
        let alert = UIAlertController(title: "提示", message: "條件成立", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
        alert.addAction(UIAlertAction(title: "Sign out", style: .destructive, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
}
複製代碼


ViewModel

import UIKit
import Foundation
@objc protocol ViewModelDelegate {
    //外部響應
    //在內部實現,方法過程在外部
    func reloadViews()//
    func alertInfo()
    func moveToHomeScreen()
}

class ViewModel: NSObject {
    weak var delegate: ViewModelDelegate?
    /// 這一部分是須要ViewController調用的地方
    var userColor: UIColor? {
        return datedUsername ? UIColor.green : UIColor.red
    }
    var passColor: UIColor? {
        return datedPassword ? UIColor.green : UIColor.red
    }
    var repeatpassColor: UIColor? {
        return datedPasswordRepeated ? UIColor.green : UIColor.red
    }
    var loginButtonBack: UIColor? {
        return datedUsername && datedPassword && datedPasswordRepeated ? UIColor.black : UIColor.white
    }
    var loginButtontextColor: UIColor? {
        return datedUsername && datedPassword && datedPasswordRepeated ? UIColor.white : UIColor.black
    }
    //當三個條件都成立
    var loginButtonEnable: Bool {
        return datedUsername && datedPassword && datedPasswordRepeated
    }
    var preate: String!
    // 輸出
    var datedUsername: Bool! = false
    var datedPassword: Bool! = false
    var datedPasswordRepeated: Bool! = false

    /*
     viewModel判斷條件
    */
    func validatedUsername(user: String?) {
        if (user?.count)! <= 0 || (user?.count)! > 6 {
            datedUsername = false
        }else {
            datedUsername = true
        }
        delegate?.reloadViews()
    }
    func validatedPassword(pass: String?) {
        preate = pass
        if (pass?.count)! <= 0 || (pass?.count)! > 6 {
            datedPassword = false
        }else {
            datedPassword = true
        }
        delegate?.reloadViews()
    }
    func validatedPasswordRepeated(repeatpass: String?) {
        if repeatpass != preate {
            datedPasswordRepeated = false
        }else {
            datedPasswordRepeated = true
        }
        delegate?.reloadViews()
    }
    func login() {
        delegate?.moveToHomeScreen()
    }
}複製代碼

最後

按照咱們看到的,ViewModel作了哪些事:

// c輸出  (UI響應)
var userColor: UIColor? { get }
var passColor: UIColor? { get }
var repeatpassColor: UIColor? {get}
var loginButtonBack: UIColor? {get}
var loginButtontextColor: UIColor? {get}
var loginButtonEnable: Bool { get }
複製代碼

// 輸入 (邏輯處理)
func validatedUsername(user: String?)
func validatedPassword(pass: String?)
func validatedPasswordRepeated(repeatpass: String?)
func login()複製代碼

//數據綁定過程
protocol ViewModelDelegate複製代碼
相關文章
相關標籤/搜索