从MVC到MVVM(一)



MVC

任何一个正经开发过一阵子软件的人都熟悉MVC. 它意思是Model View Controller, 其用意在于将数据与视图分离开来, 是一个在复杂应用设计中组织代码的公认模式. 它也被证实在 iOS 开发中有着第二种含义: Massive View Controller(重量级视图控制器).

为便于理解, 这里截取一张来iOS MVC 示意图:

从MVC到MVVM(一)_第1张图片
Paste_Image.png
  1. 图中,绿色的箭头表示直接引用。 对View 的直接引用体现在 IBOutlet 上。 当引用一个View 时,比如Button。 需要在ViewController中声明一个IBOutlet UIButton * btn

  2. 然后,我们看View 是怎么向 Controller 通信的。对于这个, iOS 有三种常见的模式:

    设置View对应的Action Target。如设置UIButtonTouch up insideAction Target

    设置ViewDelegate,如UIAlertViewDelegate, UIActionSheetDelegate,UITextFieldDelegate等。

    设置Viewdata source, 如UITableViewDataSource

    通过以上三种模式,View既能向Controller通信,又无需知道具体的Controller是谁,这样,View 就与Controller解耦了。

  3. 除此之外, iOS 还提供了 Action-Target 模式来让Controller 监听View 触发的事件。 View 又是如何获取数据呢? iOS提供了 Data source 的概念,其实也就是Protocol 的应用。

预设场景

下面我会分别使用我所理解的MVC模式来完成一个应用场景:

我们选取最常见的一组场景: 根据某种规则获取一组数据,点击某一条数据,可以跳转到下一界面获取数据详情.这里我会根据分类请求此分类下的博客列表,点击某一条信息,可跳转到博客详情页.简单说,其实我们真正需要实现的只有两个页面: 博客分类列表页 与 博客详情页.

数据接口

  • 博客列表接口
    http://www.ios122.com/find_php/index.php?viewController=YFPostListViewController&model[category]=ui&model[page]=2
[
    {
        "id": "ui_40",
        "title": "title_ui_40",
        "desc": "desc_ui_40"
    },
    {
        "id": "ui_41",
        "title": "title_ui_41",
        "desc": "desc_ui_41"
    },
    {
        "id": "ui_42",
        "title": "title_ui_42",
        "desc": "desc_ui_42"
    },
    {
        "id": "ui_43",
        "title": "title_ui_43",
        "desc": "desc_ui_43"
    },
    {
        "id": "ui_44",
        "title": "title_ui_44",
        "desc": "desc_ui_44"
    },
    {
        "id": "ui_45",
        "title": "title_ui_45",
        "desc": "desc_ui_45"
    },
    {
        "id": "ui_46",
        "title": "title_ui_46",
        "desc": "desc_ui_46"
    },
    {
        "id": "ui_47",
        "title": "title_ui_47",
        "desc": "desc_ui_47"
    },
    {
        "id": "ui_48",
        "title": "title_ui_48",
        "desc": "desc_ui_48"
    },
    {
        "id": "ui_49",
        "title": "title_ui_49",
        "desc": "desc_ui_49"
    },
    {
        "id": "ui_50",
        "title": "title_ui_50",
        "desc": "desc_ui_50"
    },
    {
        "id": "ui_51",
        "title": "title_ui_51",
        "desc": "desc_ui_51"
    },
    {
        "id": "ui_52",
        "title": "title_ui_52",
        "desc": "desc_ui_52"
    },
    {
        "id": "ui_53",
        "title": "title_ui_53",
        "desc": "desc_ui_53"
    },
    {
        "id": "ui_54",
        "title": "title_ui_54",
        "desc": "desc_ui_54"
    },
    {
        "id": "ui_55",
        "title": "title_ui_55",
        "desc": "desc_ui_55"
    },
    {
        "id": "ui_56",
        "title": "title_ui_56",
        "desc": "desc_ui_56"
    },
    {
        "id": "ui_57",
        "title": "title_ui_57",
        "desc": "desc_ui_57"
    },
    {
        "id": "ui_58",
        "title": "title_ui_58",
        "desc": "desc_ui_58"
    },
    {
        "id": "ui_59",
        "title": "title_ui_59",
        "desc": "desc_ui_59"
    }
]
  • 博客详情接口
    http://www.ios122.com/find_php/index.php?viewController=YFPostViewController&model[id]=ui_0
{
    "title": "title of ui_0",
    "body": "

Hello iOS122

Scann To Join Us
\"qq\"" }

思路分析

  • 分类列表页面:
    在前一页面指定博客分类;
    页面加载时自动发起网络请求获取对应分类的数据;
    获取数据成功后,自动刷新视图;获取失败,则给出错误提示;
    点击某一条数据,可跳转到博客详情页.
  • 详情页面:
    在前一页面指定博客id;
    页面加载时自动发起网络请求获取id的博客详情;
    获取成功后,自动刷新视图;获取失败,则给出错误提示.

代码展示

//
//  MVCListViewController.m
//  MVVM_Demo
//
//  Created by felix on 16/5/24.
//  Copyright © 2016年 Felix.He. All rights reserved.
//

#import "MVCListViewController.h"

#import "MVCListModel.h"

@interface MVCListViewController ()

#pragma mark - 属性
@property (nonatomic,strong) UITableView *listTableView;
/** 数据源 */
@property (nonatomic,strong) NSMutableArray *dataArray;
/** 数据页数 */
@property (nonatomic,assign) NSInteger page;

@end

@implementation MVCListViewController

#pragma mark - 生命周期
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.title = @"MVC";
    
    self.view.backgroundColor = [UIColor whiteColor];
    
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    
    // 马上进入刷新状态
    [self.listTableView.mj_header beginRefreshing];
}

#pragma mark - tableView代理方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.dataArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"CellID";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID
                ];
    }
    
    MVCListModel *listModel = self.dataArray[indexPath.row];
    
    cell.textLabel.text = [NSString stringWithFormat:@"标题:%@ 内容:%@",listModel.title,listModel.desc];
    
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    
}

#pragma mark - 更新数据
/** 更新数据 */
- (void)updateData
{
    MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hud.mode = MBProgressHUDModeIndeterminate;
    NSString * urlStr = [NSString stringWithFormat: @"http://www.ios122.com/find_php/index.php?viewController=YFPostListViewController&model[category]=%@&model[page]=%ld", @"ui", (long)self.page ++];
    [HJFNetworking getWithUrl:urlStr success:^(id response) {
        [self.listTableView.mj_header endRefreshing];
        [self.listTableView.mj_footer endRefreshing];
        
        [hud hide:YES];
        
        if (self.page == 1) {
            // 说明是在重新请求数据
            self.dataArray = nil;
        }
        
        NSArray *tempArray = [MVCListModel mj_objectArrayWithKeyValuesArray:response];
        
        [self.dataArray addObjectsFromArray:tempArray];
        
        [self.listTableView reloadData];
        
    } fail:^(NSError *error) {
        [self.listTableView.mj_header endRefreshing];
        [self.listTableView.mj_footer endRefreshing];
        
        hud.mode = MBProgressHUDModeText;
        hud.labelText = @"您的网络不给力";
        [hud hide:YES afterDelay:2];
    }];
}

#pragma mark - 懒加载
- (UITableView *)listTableView {
    if (!_listTableView) {
        _listTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
        [self.view addSubview:_listTableView];
        [_listTableView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.mas_equalTo(UIEdgeInsetsMake(0, 0, 0, 0));
        }];
        _listTableView.delegate = self;
        _listTableView.dataSource = self;
        
        _listTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
            self.page = 0;
            [self updateData];
        }];
        _listTableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
            [self updateData];
        }];
    }
    return _listTableView;
}

- (NSMutableArray *)dataArray {
    if (!_dataArray) {
        _dataArray = [NSMutableArray array];
    }
    return _dataArray;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end

你可能感兴趣的:(从MVC到MVVM(一))