MVVM簡介:
Model: 用來表示需要的數(shù)據(jù),不包含邏輯
View:與用戶直接交互窒升,只需處理觸發(fā)事件后的轉(zhuǎn)發(fā)缀遍,和告訴他如何顯示
ViewModel:跟蹤view的事件,處理model層傳遞的數(shù)據(jù)饱须;公開方法域醇、屬性,讓view保持最新的狀態(tài)
以下示例中:如有model蓉媳,可用來容納登錄信息
1642839667675016.gif
狀態(tài)變更之后譬挚,ViewModel 可通過委托代理或閉包通知view重新加載視圖。
此處用的是閉包
LoginView代碼:
@IBOutlet weak var phoneTfield: UITextField!
@IBOutlet weak var verCodeTfield: UITextField!
@IBOutlet weak var verBtn: UIButton!
@IBOutlet weak var loginBtn: UIButton!
var vm:LoginViewModel = LoginViewModel()
var viewModel: LoginViewModel {
set {
vm = newValue
weak var weakself = self
vm.reloadLoginColor = {(loginValid) in
weakself?.loginBtn.backgroundColor = weakself?.vm.loginBackgroundColor
weakself?.loginBtn.isEnabled = !loginValid
}
vm.reloadVerColor = {(verBtnValid,title) in
weakself?.verBtn.layer.borderColor = weakself?.vm.verBtnBorderColor?.cgColor
weakself?.verBtn.setTitle(title, for: .normal)
weakself?.verBtn.setTitleColor(weakself?.vm.verBtnBorderColor, for: .normal)
weakself?.verBtn.isEnabled = !verBtnValid
}
}
get {
vm
}
}
override func awakeFromNib() {
phoneTfield.addTarget(self, action: #selector(phonetfieldChange(textfield:)), for: UIControl.Event.editingChanged)
verCodeTfield.addTarget(self, action: #selector(vertfieldChange(textfield:)), for: UIControl.Event.editingChanged)
}
@objc func phonetfieldChange(textfield: UITextField) {
viewModel.phoneTextfieldChanged(textfield: textfield)
}
@objc func vertfieldChange(textfield: UITextField) {
viewModel.verTextfieldChanged(textfield: textfield)
}
@IBAction func verCodeBtnClick(_ sender: UIButton) {
viewModel.clickVerCode(btn: sender)
}
@IBAction func loginBtnClick(_ sender: UIButton) {
viewModel.login()
}
deinit {
viewModel.clearTimer()
}
ViewModel 中暴露的屬性必須是與View需要的相匹配
LoginViewModel代碼:
class LoginViewModel: NSObject {
var loginHome : (()->())?
var reloadLoginColor : ((Bool)->())?
var reloadVerColor : ((Bool,String)->())?
private var timer: Timer?
private var seconds: Int = 60
private var phoneStr: String?
private var verCodeStr: String?
private var loginValid = true
private var verBtnValid = true
private var isCountDownn: Bool = false//記錄是否正在倒計時
var loginBackgroundColor: UIColor? {
loginValid ? .lightGray : UIColor(red: 0.04, green: 0.71, blue: 1, alpha: 1)
}
var verBtnBorderColor: UIColor? {
verBtnValid ? .lightGray : UIColor(red: 0.04, green: 0.71, blue: 1, alpha: 1)
}
//登錄
func login(){
guard let str = phoneStr,str.count == 11 else {
print("請輸入手機號")
return
}
guard let str = verCodeStr,str.count == 6 else {
print("請輸入驗證碼")
return
}
loginHome?()//使用delegate或閉包都可以
}
//獲取驗證碼
func clickVerCode(btn: UIButton){
verBtnValid = true
isCountDownn = true
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(countDownGetCode), userInfo: nil, repeats: true)
RunLoop.main.add(timer!, forMode: .common)
}
//倒計時
@objc func countDownGetCode(){
seconds -= 1
if seconds == 0 {
seconds = 60
verBtnValid = false
isCountDownn = false
reloadVerColor?(verBtnValid,"獲取驗證碼")
clearTimer()
}else{
reloadVerColor?(verBtnValid,String(seconds)+"s")
}
}
func clearTimer() {
if (timer != nil) {
timer?.invalidate()
timer = nil
}
}
//監(jiān)聽手機號輸入框
func phoneTextfieldChanged(textfield: UITextField) {
if let str = textfield.text,str.count > 11 {
let phoneIndex = str.index(str.startIndex, offsetBy: 11)
textfield.text = String(str[str.startIndex..<phoneIndex])
}
phoneStr = textfield.text
loginValid = (phoneStr?.count ?? 0) == 11 && (verCodeStr?.count ?? 0) == 6 ? false : true
reloadLoginColor?(loginValid)
if !isCountDownn {
//倒計時完成才能再一次獲取驗證碼
verBtnValid = (phoneStr?.count ?? 0) == 11 ? false : true
reloadVerColor?(verBtnValid,"獲取驗證碼")
}
}
//監(jiān)聽驗證碼輸入框
func verTextfieldChanged(textfield: UITextField) {
if let str = textfield.text,str.count > 6 {
let phoneIndex = str.index(str.startIndex, offsetBy: 6)
textfield.text = String(str[str.startIndex..<phoneIndex])
}
verCodeStr = textfield.text
loginValid = (phoneStr?.count ?? 0) == 11 && (verCodeStr?.count ?? 0) == 6 ? false : true
reloadLoginColor?(loginValid)
}
}
ViewController(在MVVM中也可稱為View)代碼:
class ViewController: UIViewController {
var viewModel: LoginViewModel = LoginViewModel()
lazy var loginv: LoginView = {
let view = LoginView.loadFromNib()
view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(loginv)
loginv.viewModel = viewModel
weak var weakself = self
viewModel.loginHome = {() in
weakself?.present(HomeViewController(), animated: true) {
}
}
}
}