美文网首页iOS备忘录
iOS-MVVM 模式简单演练

iOS-MVVM 模式简单演练

作者: 一位不愿透露姓名的王先生_ | 来源:发表于2017-12-03 15:20 被阅读96次

本篇只是简单介绍一下MVVM的大致模式,每个人对每种架构模式有自己的理解,本文也是单纯的从获取新闻列表数据,并将其显示到界面上而已。暂时不做过多的考虑。


MVVM 简介

看下MVVM大致模式图 :

相当于在ViewViewControllerModel之间多了一层ViewModel。那么多出这层起到了什么作用呢?好处又好在哪里呢?

简单说就是如果数据结构有变动,而View层没有变动的话,那么只要处理ViewModel中的业务逻辑就可以了。ViewModel的主要作用就是处理数据处理一些小的业务逻辑等一些作用。

下面以一个新闻列表为例,展示结果如下图 :


文件结构

- Main
    - AppDelegate.h
    - AppDelegate.m
    - main.m
- News
    - Controller
        - QQNewsViewController.h
        - QQNewsViewController.m
    - ViewModel
        - QQNewsListViewModel.h
        - QQNewsListViewModel.m
        - QQNewsViewModel.h
        - QQNewsViewModel.m
    - View
        - QQNewsCell.h
        - QQNewsCell.m
    - Model
        - QQNews.h
        - QQNews.m
- Tool
    - NetWork
        - QQNetworkManager.h
        - QQNetworkManager.m
        - QQNetworkManager+QQNews.h
        - QQNetworkManager+QQNews.m

文件结构说明

  • QQNewsViewModel 单条新闻视图模型(处理业务逻辑)
  • QQNewsListViewModel 新闻列表视图模型(获取数据、加工数据)
  • QQNetworkManager网络请求工具类
  • QQNetworkManager+QQNews网络请求工具类的分类(专门用于获取新闻列表数据)

获取数据

QQNetworkManager的分类QQNetworkManager+QQNews中定义一个加载新闻数据的方法,供外界调用。

拿到的数据后,简单处理一下,回调到QQNewsListViewModel中。

#import "QQNetworkManager.h"

@interface QQNetworkManager (QQNews)

- (void)loadNewsDataCompletion:(void (^)(NSArray *dataArray))completion;

@end
#import "QQNetworkManager+QQNews.h"

static NSString *const newsURLString = @"http://c.m.163.com/nc/article/headline/T1348647853363/0-10.html";

@implementation QQNetworkManager (QQNews)

- (void)loadNewsDataCompletion:(void (^)(NSArray *))completion {
    
    [[QQNetworkManager sharedManager] qq_request:GET urlString:newsURLString parameters:nil finished:^(id result, NSError *error) {
        if (error) {
            NSLog(@"%s %@", __FUNCTION__, error);
        }
        NSLog(@"%s %@", __FUNCTION__, result);
        
        /**
         * 简单处理数据,只把需要的回调到`QQNewsListViewModel`中
         */
        completion(result[@"T1348647853363"]);
    }];
}

@end

QQNewsListViewModel.h中定义新闻视图模型数组,用来控制QQNewsViewControllerTableView显示的Cell行数及Cell的显示内容。

QQNewsListViewModel.h

@class QQNewsViewModel;

@interface QQNewsListViewModel : NSObject

/// 新闻`视图模型`数组
@property (nonatomic, strong) NSMutableArray *newsList;

/**
 加载新闻数据

 @param completion completion
 */
- (void)loadNewsDataCompletion:(void (^)(BOOL isSuccessed))completion;

@end

QQNewsListViewModel.m中进行数据加工,将拿到的字典数组转化为视图模型数组

QQNewsListViewModel.m

- (void)loadNewsDataCompletion:(void (^)(BOOL))completion {
    
    // 调用`QQNetworkManager+QQNews`中的获取新闻数据的方法
    [[QQNetworkManager sharedManager] loadNewsDataCompletion:^(NSArray *dataArray) {
        
        NSLog(@"%s %@", __FUNCTION__, dataArray);
        
        NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:dataArray.count];
        
        for (NSDictionary *dict in dataArray) {
            
            QQNews *news = [QQNews mj_objectWithKeyValues:dict];
            [arrayM addObject:[QQNewsViewModel viewModelWithNews:news]];
        }
        
        [self.newsList addObjectsFromArray:arrayM];
        
        // 将结果回调给`QQNewsViewController`,使其进行刷新界面等操作
        completion(YES);
    }];
}

接下来QQNewsViewController中就只持有QQNewsListViewModel,基本主要方法就剩下了TableViewDataSourceDelegate方法了。

@interface QQNewsViewController ()<UITableViewDataSource, UITableViewDelegate>

/// TableView
@property (nonatomic, strong) UITableView *tableView;
/// 新闻视图模型数组
@property (nonatomic, strong) QQNewsListViewModel *newsListViewModel;

@end

@implementation QQNewsViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setupUI];
    [self loadData];
}

#pragma mark - Load Data
- (void)loadData {
    
    [self.newsListViewModel loadNewsDataCompletion:^(BOOL isSuccessed) {
        
        if (!isSuccessed) {
            NSLog(@"%s 没有请求到数据", __FUNCTION__);
        }
        [self.tableView reloadData];
    }];
}

#pragma mark - SetupUI
- (void)setupUI {
    
    self.navigationItem.title = @"新闻列表";
    [self tableView];
}

#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.newsListViewModel.newsList.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    QQNewsCell *cell = [QQNewsCell newsCellWithTableView:tableView];
    cell.viewModel = self.newsListViewModel.newsList[indexPath.row];
    return cell;
}

QQNewsCell里赋值的方法就可以简化成如下 :

- (void)setViewModel:(QQNewsViewModel *)viewModel {
    _viewModel = viewModel;
    
    [self.newsImageView sd_setImageWithURL:viewModel.imgsrc_url];
    self.newsTitleLabel.text = viewModel.news.title;
    self.newsSubTitleLabel.text = viewModel.news.digest;
    self.replyCountLabel.text = viewModel.replyCount_string;
}

imgsrc_urlreplyCount_string都是根据拿到的数据在QQNewsViewModel里面加工处理过的。QQNewsCell不需要关心返回数据需要经过再次处理的事情,安心展示界面就OK了。

接下来QQNewsViewModel就来处理各种业务逻辑了,比如时间跟帖数等等一切不能直接把数据直接显示,需要再加工的事情。

QQNewsViewModel.h

/// 新闻数据模型
@property (nonatomic, strong) QQNews *news;
/// 新闻图片URL
@property (nonatomic, strong) NSURL *imgsrc_url;
/// 跟帖数(在此处理)
@property (nonatomic, copy) NSString *replyCount_string;

+ (instancetype)viewModelWithNews:(QQNews *)news;

处理业展示的数据,并可以直接对一些情况方便的进行测试,比如查看跟帖数大于1万人时的显示等等...

+ (instancetype)viewModelWithNews:(QQNews *)news {
    
    QQNewsViewModel *viewModel = [[self alloc] init];
    
    viewModel.news = news;
    
    return viewModel;
}

- (NSURL *)imgsrc_url {
    
    return [NSURL URLWithString:self.news.imgsrc];
}

- (NSString *)replyCount_string {
    
    // 测试跟帖数超过1万
//    self.news.replyCount = 23456;
    
    if (self.news.replyCount >= 10000) {
        
        NSString *string = [NSString stringWithFormat:@"%ld万 跟帖", self.news.replyCount / 10000];
        return string;
    }
    return [NSString stringWithFormat:@"%ld 跟帖", self.news.replyCount];
}

如果没有ViewModel层的话,这些事情基本都是要放在CellsetModel方法里面去做。如果Cell界面够复杂的话,Cell内的代码就会超级多。也不是不行,只是不太便于我们进行修改测试等。

本文这种情况用这种MVVM模式无疑是有点用力过猛了。但是我觉得实际开发中,很少能碰到这么简单的业务逻辑的,如果业务逻辑多起来了。这种模式就会突显出它的好处了。

Demo 传送门 : MVVM

相关文章

网友评论

    本文标题:iOS-MVVM 模式简单演练

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