MVVM 和 路由层

2018-08-28  本文已影响75人  简_爱SimpleLove

MVVM

MVVM是一种减轻MVC中控制器Controller负担的一种模式,其中,M是指Model,V是指ViewController/View,VM则是指ViewModel。
其中MVC模式是下面这种的:


MVC

MVVM模式是下面这种的:


MVVM
MVP

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架构

上一篇下一篇

猜你喜欢

热点阅读