MVVM 和 路由層
MVVM
MVVM是一種減輕MVC中控制器Controller負擔的一種模式,其中,M是指Model,V是指ViewController/View,VM則是指ViewModel。
其中MVC模式是下面這種的:
MVVM模式是下面這種的:
Model 有DataModel(純資料), LogicModel(資料邏輯處理)兩種,純資料Model就是指MVVM中的“M”,資料的邏輯處理Model就是指viewModel。這裡是每一個view都可以對應一個viewModel,從而把原來在控制器controller中處理的資料邏輯都放到了viewModel中。
控制器controller的view也可以對應一個viewModel。
應用如下:
controller控制器中程式碼:
/*
V - VM
MVCC (多控制器)addChildViewController
對業務的拆分(細分c)
*/
@interface RecommendVCtr ()
@property (nonatomic, strong)RecommendViewModel *recomendViewModel;
@property (nonatomic, strong)ReTableViewModel *reTableViewModel;
@end
@implementation RecommendVCtr{
ReMenuMoreVCtr *_reMenuMoreVCtr;
}
- (void)viewDidLoad {
[super viewDidLoad];
//self addChildViewController:<#(nonnull UIViewController *)#>
self.automaticallyAdjustsScrollViewInsets = NO;
[_headView removeFromSuperview];
[_tableView setTableHeaderView:_headView];
// [self.reTableViewModel configTable:_tableView];
UITableView *tmpTableV = _tableView;
// 下載資料業務
[self.recomendViewModel loadDatafromNetWithPage:1 finishNet:^(id infoDict) {
[tmpTableV reloadData];
}];
}
- (void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
[self.view bringSubviewToFront:_tableView];
_tableView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - SysNavHeigh - SysTabBarHeigh);
}
- (IBAction)removeSelectV:(UIButton*)sender{
[self.recomendViewModel deleteAdView:_isDeleteAdView headView:_headView tableview:_tableView];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
// return [RecommendCell cellHeight];
return 80.0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.recomendViewModel.rowNumber;
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
RecommendCell *cell = [tableView dequeueReusableCellWithIdentifier:@"RecommendCell"];
if (!cell) {
cell = [[[NSBundle mainBundle] loadNibNamed:@"RecommendCell" owner:nil options:nil] firstObject];
}
cell.messageModel = [self messageModelForRow:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self.recomendViewModel pushMessageDetailIndex:indexPath viewCtr:self];
}
- (MessageModel*)messageModelForRow:(NSInteger)row{
// if (row < self.messageAry.count) {
// return self.messageAry[row];
// }else{
// NSLog(@"row越界messageAry:%ld--%ld", row, _messageAry.count);
// }
return nil;
}
#pragma mark - lazy loading
- (RecommendViewModel*)recomendViewModel{
if (!_recomendViewModel) {
_recomendViewModel = [RecommendViewModel new];
}
return _recomendViewModel;
}
- (ReTableViewModel*)reTableViewModel{
if (!_reTableViewModel) {
_reTableViewModel = [ReTableViewModel new];
}
return _reTableViewModel;
}
Model:
#import <Foundation/Foundation.h>
@interface MessageModel : NSObject
@property (nonatomic, strong)NSString *messageId;
@property (nonatomic, strong)NSString *messageTitle;//標題
@property (nonatomic, strong)NSString *statusStr; // 是否頭條/重播/
@property (nonatomic, strong)NSString *commentCount;//評論數
@property (nonatomic, strong)NSString *fromCompany;//來自哪個 如66車訊/車視集
@property (nonatomic, assign)int layoutStyle; // 左右佈局/上下佈局
@property (nonatomic, assign)float messageHeight;
@end
#import "MessageModel.h"
@implementation MessageModel
- (float)messageHeight{
return 80.0;
}
@end
viewModel:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class MessageModel;
typedef void(^finishLoadBlock)(id infoDict);
@interface RecommendViewModel : NSObject
@property(nonatomic, assign) NSInteger rowNumber;
- (CGFloat)messageHeightForRow:(NSInteger)row;
- (MessageModel*)messageModelForRow:(NSInteger)row;
- (NSString*)messageIdForRow:(NSInteger)row;
- (void)loadDatafromNetWithPage:(NSInteger)page finishNet:(finishLoadBlock)finishBlock;
- (void)deleteAdView:(UIView*)deleteView headView:(UIView*)headView tableview:(UITableView*)tableView;
- (void)pushMessageDetailIndex:(NSIndexPath *)indexPath viewCtr:(UIViewController*)targetVCtr;
@end
#import "RecommendViewModel.h"
#import "MessageModel.h"
#import "MessageDetailViewCtr.h"
@interface RecommendViewModel (){
}
@property (nonatomic, strong)NSMutableArray *messageAry;
@end
@implementation RecommendViewModel
/*
*/
- (NSInteger)rowNumber{
return self.messageAry.count;
}
- (CGFloat)messageHeightForRow:(NSInteger)row{
if (row < self.messageAry.count){
MessageModel *messageModel = self.messageAry[row];
return messageModel.messageHeight;
}
return 0;
}
- (MessageModel*)messageModelForRow:(NSInteger)row{
if (row < self.messageAry.count) {
return self.messageAry[row];
}else{
NSLog(@"row越界messageAry:%ld--%ld", row, _messageAry.count);
}
return nil;
}
- (NSString*)messageIdForRow:(NSInteger)row{
if (row < self.messageAry.count) {
MessageModel *messageModel = self.messageAry[row];
return messageModel.messageId;
}else{
NSLog(@"row越界messageAry:%ld--%ld", row, _messageAry.count);
}
return nil;
}
- (void)deleteAdView:(UIView*)deleteView headView:(UIView*)headView tableview:(UITableView*)tableView{
[deleteView removeFromSuperview];
headView.frame = ({
CGRect frame = headView.frame;
frame.size.height = frame.size.height - deleteView.frame.size.height;
frame;
});
[tableView setTableHeaderView:nil];
[tableView setTableHeaderView:deleteView];
// [tableView setTableHeaderView:headView];
}
- (void)pushMessageDetailIndex:(NSIndexPath *)indexPath viewCtr:(UIViewController*)targetVCtr{
// 將MessageID傳過去,那邊好根據MessageID展示資訊詳情內容
MessageDetailViewCtr *messageDetailVCtr = [[MessageDetailViewCtr alloc] initWithMessageID:[self messageIdForRow:indexPath.row]];
[targetVCtr.navigationController pushViewController:messageDetailVCtr animated:YES];
}
- (void)loadDatafromNetWithPage:(NSInteger)page finishNet:(finishLoadBlock)finishBlock{
for (int i = 0; i< 10; i++) {
MessageModel *messageModel = [MessageModel new];
messageModel.messageTitle = [NSString stringWithFormat:@"訊息::%d", i];
[self.messageAry addObject:messageModel];
}
finishBlock(nil);
}
#pragma mark - lazy loading
- (NSMutableArray*)messageAry{
if (!_messageAry) {
_messageAry = [NSMutableArray array];
}
return _messageAry;
}
@end
tableView也能配一個viewModel:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface ReTableViewModel : NSObject<UITableViewDelegate, UITableViewDataSource>
@property(nonatomic, assign) NSInteger rowNumber;
- (void)configTable:(UITableView*)tableView;
@end
#import "ReTableViewModel.h"
#import "RecommendCell.h"
@implementation ReTableViewModel{
UITableView *_tableview;
}
#pragma mark - tableview delegate
- (void)configTable:(UITableView*)tableView{
_tableview = tableView;
tableView.delegate = self;
tableView.dataSource = self;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
// return [RecommendCell cellHeight];
return 80.0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.rowNumber;
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
RecommendCell *cell = [tableView dequeueReusableCellWithIdentifier:@"RecommendCell"];
if (!cell) {
cell = [[[NSBundle mainBundle] loadNibNamed:@"RecommendCell" owner:nil options:nil] firstObject];
}
// cell.messageModel = [self messageModelForRow:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
// [self.recomendViewModel pushMessageDetailIndex:indexPath viewCtr:self];
}
- (MessageModel*)messageModelForRow:(NSInteger)row{
// if (row < self.messageAry.count) {
// return self.messageAry[row];
// }else{
// NSLog(@"row越界messageAry:%ld--%ld", row, _messageAry.count);
// }
return nil;
}
@end
不管什麼模式都是對業務邏輯的拆分,我們只要能夠實現對業務邏輯進行拆分就好了,沒必要強行套用某一個模式,那樣反而變得更加複雜。我們也還可以用MVCC模式,當一個控制器中,點選某個按鈕,有新的複雜的View產生的時候,我們可以將它獨立出來,看做當前控制器的一個子控制器。
路由層
路由層的功能就和路由器有點像,比如說當我們在一個導航控制器很深的一個頁面的時候,這時我們突然要回到某個首頁(經常產品有這樣的需求,跳轉到這,再跳轉到那),這時我們就需要路由層了,有了路由層,我們可以在跳轉的時候,不用匯入標頭檔案,而且路由層是完全獨立出來的,方便管理,也還能夠複用。
下面是未完善版本的路由層,等以後有時間了,完善一下再:
總的路由層:
#import <Foundation/Foundation.h>
@interface MenuR : NSObject
- (void)perferm:(id)target sel:(SEL)sel arg:(id)arg;
- (void)perferm:(id)target sel:(SEL)sel;
@end
#import "MenuR.h"
@implementation MenuR
- (void)perferm:(id)target sel:(SEL)sel arg:(id)arg{
[target performSelector:sel withObject:arg];
}
- (void)perferm:(id)target sel:(SEL)sel{
}
@end
路由分支:
#import <Foundation/Foundation.h>
@interface MainRoute : NSObject
- (void)skipToMenu:(NSNumber*)index;
@end
#import "MainRoute.h"
#import <UIKit/UIKit.h>
@implementation MainRoute
- (void)skipToMenu:(NSNumber*)index{
UINavigationController *target = (UINavigationController*)[UIApplication sharedApplication].keyWindow.rootViewController;
target = [target.viewControllers firstObject];
SEL sel = NSSelectorFromString(@"skipToMenuIndex:");
if ([target respondsToSelector:sel]) {
[target performSelector:sel withObject:index];
}else{
NSLog(@"異常 target 找不到 skipToMenuIndex:");
}
}
@end
上面路由分支裡面skipToMenu方法中做的事情,就是判斷APP的根控制器,是否有skipToMenuIndex:這個方法,有的話就走一次那個方法,回到根控制器的某個介面。沒有的話,就丟擲一個異常,也方便找問題所在。
所以我們還需要在根控制器中實現這個方法:
#import "MainMenuTabBarVCtr.h"
#import "TabBarButtton.h"
#import "EOCBaseNavCtr.h"
@interface MainMenuTabBarVCtr ()
@end
@implementation MainMenuTabBarVCtr
- (void)skipToMenuIndex:(NSNumber*)indexN{
NSInteger index = [indexN integerValue];
[self.navigationController popToRootViewControllerAnimated:YES];
//self.selectedIndex = index;
[self selectMenuVC:[_eocTabBar viewWithTag:index]];
}
然後在需要跳轉的地方,如下:
使用總的路由的話:
MenuR *mr = [MenuR new];
[mr perferm:[NSClassFromString(@"MainRoute") new] sel:@selector(skipToMenu:) arg:[NSNumber numberWithInt:2]];
只是使用分支路由的話:
[MainRoute skipToMenu:2];
參考文章:
iOS MVVM架構
相關文章
- Django進階之路由層和檢視層Django路由
- Django 路由層Django路由
- Django路由層Django路由
- 在iOS上使用MVVM進行路由iOSMVVM路由
- django的路由層Django路由
- iOS底層原理 MVC、MVP、MVVM、分層設計淺談 — (13)iOSMVCMVPMVVM
- MVVM分層下的前端工程化開發MVVM前端
- Django框架之路由層彙總Django框架路由
- 前端學習(2576):選擇何種方式的路由和底層原理前端路由
- 小程式無限層級路由方案路由
- 繼續聊聊MVVM和元件化MVVM元件化
- [譯] 實用的 MVVM 和 RxSwiftMVVMSwift
- [譯] iOS 裡的 MVVM 和 RxSwiftiOSMVVMSwift
- MVC、MVP和MVVM的區別MVCMVPMVVM
- 二層交換機鏈路聚合、三層交換機鏈路聚合和三層交換機的單臂路由專案路由
- 靜態路由和動態路由路由
- MVC、MVP和MVVM以及MVA比較MVCMVPMVVM
- MVC,MVP 和 MVVM 模式如何選擇?MVCMVPMVVM模式
- MVVM 最佳解讀和實踐MVVM
- MVVM的學習記錄和思考MVVM
- 聊聊MVC和模組化以及MVVM和元件化MVCMVVM元件化
- MVVMMVVM
- 前端路由和後端路由,前端渲染和後端渲染前端路由後端
- MVC,MVP和MVVM之間的區別MVCMVPMVVM
- 談談對MVC、MVP和MVVM的理解?MVCMVPMVVM
- ArkUI與MVVM模式的詩和遠方UIMVVM模式
- 路由和轉發路由
- iOS基於中間層的路由跳轉方案iOS路由
- 深入底層之實現 Laravel 路由註冊功能Laravel路由
- 頂層const和底層const
- MVVM原理,你看了也會vue MVVMMVVMVue
- VIPER 和 MVVM 到底有什麼區別MVVM
- ADOV路由和DSR路由matlab對比模擬路由Matlab
- Vue2+Koa2+Typescript前後端框架教程--03後端路由和三層模式配置VueTypeScript後端框架路由模式
- Flutter路由和導航Flutter路由
- 搞懂頂層const和底層const
- 小程式無限層級路由方案(無框架依賴)路由框架
- ensp 實驗十一單臂路由 三層交換機路由