IOS 開發模式 之MVC模式
說到MVC 模式,根據意思來說 Model ,View,Controller 模型,視圖,控制。
說是這麼說,但是還是不明白到底應該怎麼弄。(這其實是說我自己的,從下面就能看出)。
Model 模型,建立一個數據模型,裏面是我們需要用到的數據,可以建立多個模型,一個模型裏面包含另一個模型。
View 視圖,創建視圖。把需要用到的控件視圖,建立一個子類。在裏面設置視圖,最後返回出來,代理一般在自己界面中是實現,一般就是自己創建的子類中實現。有時候還可以對model 的數據操作
Controller 控制器。 裏面就是一些邏輯的處理 ,把返回的View 視圖添加到界面上,刪除控件等邏輯的是實現,對Model 數據的操作。
(我目前是這樣理解的,根據MVC的定義,View只需要處理視圖,對數據Model 的處理 應該在Controller 中,我目前在View 處理數據,以後再根據理解慢慢更改)
下面粘貼一段 網上覆制過來的其他網友的話:
在iOS cocoa touch 編程中, MVC機制被髮揮得淋漓盡致。 MVC 示意圖如下。 只有充分理解了MVC,才能在編寫出優雅的iOS app。爲充分理解 MVC, 相關的概念(比如: Delegate、 Protocol、 Notification 等)也要了然於胸。
MVC 約定, Model 不允許與View 打交道。 Model 是管理數據的, 當Model中的數據發生變化時,與之對應的視圖應更新。 這就需要一種機制來支持。爲此 iOS 框架提供了兩種支持機制: Notification 和KVO (Key-Value Observing)。
KVO 可簡單理解爲,爲你所關注的 Key 對象註冊一個監聽器。 當有數據發生變化時,就會發出廣播給所有的監聽器。
MVC 也約定, View 不允許直接引用Modal, 它只能被Controller 所控制。 Controller 控制 View 顯示什麼數據。我們知道,View 所要顯示的數據是來源於 Modal, View 上產生的事件 ( 比如 Touch事件)需要通知 Controller。 既然MVC 不允許直接打交道,就需要提供一種機制。
不錯, iOS 確實提供了一種機制, 名曰: Delegate。 Delegate 這個詞, 有人將它譯爲“委託”,也有人將它譯爲“代理”。名稱上的差異沒有什麼,重要的是如何理解 Delegate。 Delegate設計模式的引入,就是爲了解決UIView與Controller松耦合互動問題。
下面的代碼 有我自己寫的 還有參考其他大神的
定義的Model
SectionModel
#import <Foundation/Foundation.h>
//分區模型
@interface SectionModel : NSObject
@property (nonatomic, copy) NSString *sectionTitle;
//是否可以展開
@property (nonatomic, assign) BOOL isExpanded;
//分區下面可以有很多個cell對應的模型
@property (nonatomic, strong) NSMutableArray *cellModels;
@end
CellModel
#import <Foundation/Foundation.h>
@interface CellModel : NSObject
@property (nonatomic, copy) NSString *title;
@end
創建View
我自定義了一個TableView 並在裏面對數據進行 處理,但是這樣不符合MVC模式 我只是根據自己理解這樣設置的
#import <UIKit/UIKit.h>
@interface TableView : UITableView
-(instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style;
@end
TableView 的實現文件 我導入了Model 模型文件
#import "TableView.h"
#import "HeaderView.h"
#import "SectionModel.h"
#import "CellModel.h"
@interface TableView ()<UITableViewDelegate,UITableViewDataSource,UIAlertViewDelegate>
@property (nonatomic,strong) NSMutableArray *dataArray;
@end
@implementation TableView
-(NSMutableArray *)dataArray
{
if (_dataArray == nil) {
_dataArray = [[NSMutableArray alloc]init];
for (NSInteger i = 0; i < 3; i++) {
//設置分區
SectionModel *sectionModel = [[SectionModel alloc]init];
sectionModel.isExpanded = NO;
sectionModel.sectionTitle = [NSString stringWithFormat:@"section = %ld",i];
//設置cell
NSMutableArray *array = [[NSMutableArray alloc]init];
for (NSInteger j = 0; j < 5; j++) {
CellModel *cellModel = [[CellModel alloc]init];
cellModel.cellTitle = [NSString stringWithFormat:@"section = %ld, row = %ld",i,j];
cellModel.webImageUrl = [NSString stringWithFormat:@""];
[array addObject:cellModel];
}
sectionModel.cellModels = array; //分區模型裏面添加cell模型
[_dataArray addObject:sectionModel]; //數據裏面添加分區模型
}
}
return _dataArray;
}
-(instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
{
if (self = [super initWithFrame:frame style:style]) {
self.delegate = self;
self.dataSource = self;
self.showsVerticalScrollIndicator = NO;
self.showsHorizontalScrollIndicator = NO;
[self registerClass:[HeaderView class] forHeaderFooterViewReuseIdentifier:headerIdentifier];
}
return self;
}
#pragma mark UITableViewDataSource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return self.dataArray.count;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
SectionModel *sectionMocel = self.dataArray[section];
return sectionMocel.isExpanded ? sectionMocel.cellModels.count : 0;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = @"TableView";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
UIView *selectBg = [[UIView alloc]initWithFrame:cell.frame];
selectBg.backgroundColor = [UIColor redColor];
cell.selectedBackgroundView = selectBg;
cell.backgroundColor = [UIColor grayColor];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
SectionModel *sectionModel = self.dataArray[indexPath.section];
CellModel *cellModel = sectionModel.cellModels[indexPath.row];
cell.textLabel.text = cellModel.cellTitle;
return cell;
}
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
#pragma mark UITableViewDelegate
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 44;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return headerHeight;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
HeaderView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:headerIdentifier];
SectionModel *sectionModle = self.dataArray[section];
headerView.model = sectionModle;
headerView.expandCallBack = ^(BOOL isExpanded){
[tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationFade];
};
return headerView;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
SectionModel *sectionModel = self.dataArray[indexPath.section];
CellModel *cellModel = sectionModel.cellModels[indexPath.row];
NSLog(@"點擊到的是%@",cellModel.cellTitle);
[self alert:sectionModel.sectionTitle message:cellModel.cellTitle];
}
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete){
SectionModel *sectionModel = self.dataArray[indexPath.section];
[sectionModel.cellModels removeObjectAtIndex:indexPath.row];
NSLog(@"刪除");
}
[tableView reloadData];
}
-(void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"結束編輯");
}
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
{
return @"點擊刪除";
}
-(void)alert:(NSString *)title message:(NSString *)messsage
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:title message:messsage delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定", nil];
[alert show];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
//確定 爲1 取消 爲0
switch (buttonIndex) {
case 0:
NSLog(@"0");
break;
case 1:
NSLog(@"1");
break;
default:
break;
}
}
Controller
Controller 文件 裏面就比較簡單了
#import "TableView.h" //導入子類文件
- (void)viewDidLoad {
[super viewDidLoad];
UITableView *tableView = [[TableView alloc]initWithFrame:CGRectMake(0, 100, View_Width, 300) style:UITableViewStylePlain];
[self.view addSubview:tableView];
}
我自定義表頭
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
@class SectionModel;
FOUNDATION_EXPORT NSString *headerIdentifier;
FOUNDATION_EXPORT CGFloat headerHeight;
typedef void(^HeaderViewExpandCallBack)(BOOL isExpanded);
@interface HeaderView : UITableViewHeaderFooterView
@property (nonatomic,strong) SectionModel *model;
@property (nonatomic,copy) HeaderViewExpandCallBack expandCallBack;
-(instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier;
@end
表頭文件的是實現
#import "HeaderView.h"
#import "SectionModel.h"
NSString *headerIdentifier = @"headerIdentifier";
CGFloat headerHeight = 44;
@interface HeaderView ()
@property (nonatomic,strong) UIImageView *arrowImageView;
@property (nonatomic,strong) UILabel *titleLabel;
@end
@implementation HeaderView
-(instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
CGFloat width = [UIScreen mainScreen].bounds.size.width;
self.arrowImageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"back1.png"]];
self.arrowImageView.frame = CGRectMake(0, (44-8)/2, 15, 8);
[self.contentView addSubview:self.arrowImageView];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(onExpand:) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:button];
button.frame = CGRectMake(0, 0, width, 44);
self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(35, 0, 200, 44)];
self.titleLabel.textColor = [UIColor blackColor];
self.titleLabel.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:self.titleLabel];
self.contentView.backgroundColor = [UIColor greenColor];
UIView *line = [[UIView alloc]initWithFrame:CGRectMake(0, 43, width, 1)];
line.backgroundColor = [UIColor lightGrayColor];
[self.contentView addSubview:line];
}
return self;
}
//我們在配置數據的時候,也會根據isExpanded狀態來顯示圖片的方向,否則重用後就恢復原狀了。
-(void)onExpand:(UIButton *)sender
{
self.model.isExpanded = !self.model.isExpanded;
[UIView animateWithDuration:0.25 animations:^{
if (self.model.isExpanded) {
self.arrowImageView.transform = CGAffineTransformIdentity;
}else{
self.arrowImageView.transform = CGAffineTransformMakeRotation(M_PI);
}
}];
if (self.expandCallBack) {
self.expandCallBack(self.model.isExpanded);
}
}
//重寫setModel方法來配置數據 get 只讀 set 設置
-(void)setModel:(SectionModel *)model
{
if (_model != model) {
_model = model;
}
if (model.isExpanded) {
self.arrowImageView.transform = CGAffineTransformIdentity;
}else{
self.arrowImageView.transform = CGAffineTransformMakeRotation(M_PI);
}
self.titleLabel.text = model.sectionTitle;
}
@end