MVC
任何一个正经开发过一阵子软件的人都熟悉MVC. 它意思是Model View Controller, 其用意在于将数据与视图分离开来, 是一个在复杂应用设计中组织代码的公认模式. 它也被证实在 iOS 开发中有着第二种含义: Massive View Controller(重量级视图控制器).
为便于理解, 这里截取一张来iOS MVC 示意图:
图中,绿色的箭头表示直接引用。 对View 的直接引用体现在 IBOutlet 上。 当引用一个View 时,比如Button。 需要在ViewController中声明一个IBOutlet UIButton * btn;
-
然后,我们看View 是怎么向 Controller 通信的。对于这个, iOS 有三种常见的模式:
设置View对应的Action Target。如设置UIButton的Touch up inside的Action Target。
设置View的Delegate,如UIAlertViewDelegate, UIActionSheetDelegate,UITextFieldDelegate等。
设置View的data source, 如UITableViewDataSource。
通过以上三种模式,View既能向Controller通信,又无需知道具体的Controller是谁,这样,View 就与Controller解耦了。
除此之外, 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
"
}
思路分析
- 分类列表页面:
在前一页面指定博客分类;
页面加载时自动发起网络请求获取对应分类的数据;
获取数据成功后,自动刷新视图;获取失败,则给出错误提示;
点击某一条数据,可跳转到博客详情页. - 详情页面:
在前一页面指定博客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