iOS使用MVVM實現(xiàn)登錄功能

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) {
                
            }
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載酪呻,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者减宣。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市玩荠,隨后出現(xiàn)的幾起案子漆腌,更是在濱河造成了極大的恐慌,老刑警劉巖阶冈,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闷尿,死亡現(xiàn)場離奇詭異,居然都是意外死亡眼溶,警方通過查閱死者的電腦和手機悠砚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堂飞,“玉大人灌旧,你說我怎么就攤上這事绑咱。” “怎么了枢泰?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵描融,是天一觀的道長。 經(jīng)常有香客問我衡蚂,道長窿克,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任毛甲,我火速辦了婚禮年叮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘玻募。我一直安慰自己只损,他們只是感情好,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布七咧。 她就那樣靜靜地躺著跃惫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪艾栋。 梳的紋絲不亂的頭發(fā)上爆存,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音蝗砾,去河邊找鬼先较。 笑死,一個胖子當著我的面吹牛遥诉,可吹牛的內(nèi)容都是我干的拇泣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼矮锈,長吁一口氣:“原來是場噩夢啊……” “哼霉翔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起苞笨,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤债朵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后瀑凝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體序芦,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年粤咪,在試婚紗的時候發(fā)現(xiàn)自己被綠了谚中。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖宪塔,靈堂內(nèi)的尸體忽然破棺而出磁奖,到底是詐尸還是另有隱情,我是刑警寧澤某筐,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布比搭,位于F島的核電站,受9級特大地震影響南誊,放射性物質(zhì)發(fā)生泄漏身诺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一抄囚、第九天 我趴在偏房一處隱蔽的房頂上張望霉赡。 院中可真熱鬧,春花似錦怠苔、人聲如沸同廉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锅劝,卻和暖如春攒驰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背故爵。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工玻粪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诬垂。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓劲室,卻偏偏與公主長得像,于是被迫代替她去往敵國和親结窘。 傳聞我的和親對象是個殘疾皇子很洋,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容