美文网首页
MVVM 和 路由层

MVVM 和 路由层

作者: 简_爱SimpleLove | 来源:发表于2018-08-28 20:22 被阅读75次

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

    相关文章

      网友评论

          本文标题:MVVM 和 路由层

          本文链接:https://www.haomeiwen.com/subject/fagqwftx.html