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
啓動/佈局/呈現UI組件。
使用ViewModel綁定UI組件。
另外一方面,在ViewModel中,咱們作:
編寫控制器邏輯,例如分頁,錯誤處理等。
編寫表示邏輯,爲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複製代碼