从MVC到MVVM(一)



MVC

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

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

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模式来完成一个应用场景:

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

数据接口

[
    {
        "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"
    }
]
{
    "title": "title of ui_0",
    "body": "<h2>Hello iOS122</h2> Scann To Join Us <br /> <image alt=\"qq\" src=\"https://raw.githubusercontent.com/ios122/ios122/master/1443002712802.png\" />"
}

思路分析

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

#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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,061评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,092评论 4 62
  • 窗外飘着细雨,很安静,也很感动,今天我领到毕业证了,我毕业了。在很多人看来,毕业就像一个大大的句号,代表着结束。但...
    静心听风阅读 262评论 0 0
  • 解决八大误区,快速提升自律力 目的: 1.通过案例,找出有关自律误区 2.自律为什么重要 3.自律的基本原理和方法...
    up_苇叶阅读 346评论 0 1
  • 回归原点 时间:2016年7月6日 坐标:华中职能办公室 职位 :由标准组专员转换为商品部门淘品组专员 事...
    Teresa498阅读 381评论 0 3