本人有若干成套學(xué)習(xí)視頻, 可試看! 可試看! 可試看, 重要的事情說三遍 包含Java
, 數(shù)據(jù)結(jié)構(gòu)與算法
, iOS
, 安卓
, python
, flutter
等等, 如有需要, 聯(lián)系微信tsaievan
.
這里有一篇關(guān)于MVVM, 我覺得最好的解釋說明:
對于MVC架構(gòu), 即我們熟知的Model-View-Controller架構(gòu), 如下圖所示:
M層請求數(shù)據(jù), V層展示數(shù)據(jù), C層處理邏輯. 但在實際開發(fā)過程中, C層和V層是偶聯(lián)在一起的, 就形成了這種格局:
C層與M層交互, 這就往往會造成C層的邏輯過多, 代碼臃腫.
這個時候, 就產(chǎn)生了MVVM架構(gòu), 即Model-View-ViewModel架構(gòu).即在C層和M層中間加了一個ViewModel層, ViewModel就是用來為C層瘦身的. 它的出現(xiàn), 大大減少了控制器中的邏輯處理. 是C層變得輕巧.
RAC在信號傳遞方面有著得天獨厚的優(yōu)勢, 所以RAC+MVVM架構(gòu)可以說是天作之合. 今天利用RAC寫了一個登錄界面, 把C層的邏輯處理抽出來放到了ViewModel層 :
C層擁有V層和VM層:
V層和VM層的數(shù)據(jù)交互放在C層處理:
這個小Demo沒有Model, Model現(xiàn)在不直接與C層交互, 而是通過ViewModel.這樣一來, 各個類之間各行其是:
-
需求1: 賬號和密碼都填時, 登錄按鈕才可以點擊:
步驟1: View層的數(shù)據(jù)通過C層給到VM層:
- (void)loginButtonEnable {
RAC(self.loginViewModel, username) = _loginView.usernameTextField.rac_textSignal;
RAC(self.loginViewModel, password) = _loginView.passwordTextField.rac_textSignal;
}
步驟2: VM層進(jìn)行邏輯處理:
self.loginButtonEnableSignal = [RACSignal combineLatest:@[RACObserve(self, username), RACObserve(self, password)] reduce:^id(NSString *username, NSString *password){
return @(username.length && password.length);
}];
以上代碼會返回一個包裝了的BOOL值
步驟3: View層顯示數(shù)據(jù):
RAC(self.loginView.loginButton, enabled) = _loginViewModel.loginButtonEnableSignal;
給button的enabled屬性賦值, 告知按鈕是否可以點擊.
-
需求2: 響應(yīng)按鈕的點擊事件, 當(dāng)賬號密碼正確時提示正確, 并跳轉(zhuǎn)控制器, 當(dāng)賬號和密碼錯誤時, 提示錯誤.
步驟1: View層的按鈕響應(yīng)點擊事件, 并執(zhí)行VM層的命令:
[[_loginView.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
[self.loginViewModel.loginCommad execute:nil];
}];
步驟2: VM層創(chuàng)建命令:
self.loginCommad = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id input) {
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/login"]];
request.HTTPMethod = @"POST";
NSString *paramString = [NSString stringWithFormat:@"username=%@&pwd=%@&type=JSON", self.username, self.password];
NSData *paramData = [paramString dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody = paramData;
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSDictionary *resultDictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:NULL];
[subscriber sendNext:resultDictionary];
/****************** -------- 發(fā)送完成這一步很重要, 不然后面的無法信號無法執(zhí)行 -------- ******************/
[subscriber sendCompleted];
}] resume];
return nil;
}];
}];
- 我們會將處理網(wǎng)絡(luò)請求的邏輯寫在命令中.
- 創(chuàng)建命令會填入一個block參數(shù), 這個block參數(shù)會返回一個信號,在這個信號中, 我們會將請求的網(wǎng)絡(luò)數(shù)據(jù)發(fā)送出去.
- 我們需要訂閱這個信號才能拿到這個網(wǎng)絡(luò)數(shù)據(jù), 如何拿到呢?
- 在命令中有一個信號源屬性
executionSignals
- 信號源還有一個屬性
switchToLatest
讓我們可以拿到block中返回的信號,然后再訂閱這個信號, 我們就可以拿到服務(wù)器返回的數(shù)據(jù), 然后進(jìn)行登錄邏輯的處理:
- 在命令中有一個信號源屬性
[self.loginCommad.executionSignals.switchToLatest subscribeNext:^(NSDictionary *x) {
if ([x.allKeys.lastObject isEqualToString:@"success"]) {
[SVProgressHUD showSuccessWithStatus:@"登錄成功"];
[SVProgressHUD dismissWithDelay:1 completion:^{
YFFirstPageViewController *firstPageVC = [[YFFirstPageViewController alloc] init];
[UIView animateWithDuration:1 animations:^{
[UIApplication sharedApplication].keyWindow.rootViewController = firstPageVC;
}];
}];
}else {
[SVProgressHUD showErrorWithStatus:@"登錄失敗"];
[SVProgressHUD dismissWithDelay:1];
}
}];
- 還可以監(jiān)控命令的執(zhí)行過程:
[[self.loginCommad.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {
if ([x boolValue]) {
[SVProgressHUD showWithStatus:@"正在登錄中"];
}
}];
以上這些邏輯都是在VM層處理的.
在V層中, 我們只需要處理控件的布局即可, 而不需要處理任何邏輯事件:
這就是整個Demo的架構(gòu), 實現(xiàn)邏輯和代碼, 效果如下:
+++++++++++++++Demo下載鏈接+++++++++++++++密碼: nfx3