前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS-MVVM 模式简单演练

iOS-MVVM 模式简单演练

作者头像
用户1890628
发布2018-05-10 17:36:15
8240
发布2018-05-10 17:36:15
举报
文章被收录于专栏:Objective-CObjective-C

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


MVVM 简介

看下MVVM大致模式图 :

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

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

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


文件结构

代码语言:javascript
复制
- 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中。

代码语言:javascript
复制
#import "QQNetworkManager.h"

@interface QQNetworkManager (QQNews)

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

@end
代码语言:javascript
复制
#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的显示内容。

代码语言:javascript
复制
QQNewsListViewModel.h

@class QQNewsViewModel;

@interface QQNewsListViewModel : NSObject

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

/**
 加载新闻数据

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

@end

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

代码语言:javascript
复制
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方法了。

代码语言:javascript
复制
@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里赋值的方法就可以简化成如下 :

代码语言:javascript
复制
- (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就来处理各种业务逻辑了,比如时间跟帖数等等一切不能直接把数据直接显示,需要再加工的事情。

代码语言:javascript
复制
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万人时的显示等等...

代码语言:javascript
复制
+ (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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017.12.03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MVVM 简介
  • 文件结构
  • 获取数据
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档