首页 > 代码库 > [iOS基础控件 - 4.4] 进一步封装"APP列表”,初见MVC模式

[iOS基础控件 - 4.4] 进一步封装"APP列表”,初见MVC模式

A.从ViewController分离View
     之前的代码中,View的数据加载逻辑放在了总的ViewController中,增加了耦合性,应该对控制器ViewController隐藏数据加载到View的细节。
     封装View的创建逻辑
     封装View的数据加载逻辑到自定义的UIView中

 

B.思路
使用xib封装自定义view的步骤:
1.新建一个继承UIView的自定义view,这里的名字是“AppView”,用来封装独立控件组
每个AppView封装了如下图的控件组
Image(5)
2.新建一个xib文件来描述控件结构,就是上图的控件组
3.在Controller中使用AppView作为每个独立控件组的类型单位
4.将控件和View “AppView” 进行连线
5.View “AppView” 提供一个模型属性
6.重写模型属性的setter,解析模型数据
7.设置模型数据到控件中
8.自定义View “AppView”的构造方法,屏蔽读取xib文件的细节

 

其实这就是一种简单的MVC模式
Model: App.h, App.m
View: AppView.h, AppView.m
Controller: ViewController.h, ViewController.m
Controller连接了View和Model,取得数据后加载到Model,然后传给View进行解析并显示

 

C.实现
1.新建UIView类”AppView",继承自UIView
new file ==> iOS ==> Source ==> Cocoa Touch Class
创建声明文件”AppView.h”和“AppView.m”
a.
Image(6)
b.
Image(7)
c.
Image(8)
2.设置xib的class (默认是UIView) 为新建的”AppView"
Image(9)

 

3.在新建的UIView中编写View的数据加载逻辑
(1)在”AppView.h”中创建Model成员
// 在Controller和View之间传输的Model数据@property(nonatomic, strong) App *appData;
 
(2)连接控件和”AppView",创建私有控件成员
Image(10)

 

(3)在”AppView.m”中解析加载数据
- (void)setAppData:(App *)appData {_appData = appData;// 1.设置图片self.iconView.image = [UIImage imageNamed:appData.icon];// 2.设置名字self.nameLabel.text = appData.name;}

 

(4)自定义构造方法
AppView.h:
// 自定义将Model数据加载到View的构造方法- (instancetype) initWithApp:(App *) appData;// 自定义构造的类方法+ (instancetype) appViewWithApp:(App *) appData;// 返回一个不带Model数据的类构造方法+ (instancetype) appView;AppView.m:// 自定义将Model数据加载到View的构造方法- (instancetype) initWithApp:(App *) appData {// 1.从NIB取得控件UINib *nib = [UINib nibWithNibName:@"app" bundle:[NSBundle mainBundle]];NSArray *viewArray = [nib instantiateWithOwner:nil options:nil];AppView *appView = [viewArray lastObject];// 2.加载Model    appView.appData =http://www.mamicode.com/ appData;return appView;}// 自定义构造的类方法+ (instancetype) appViewWithApp:(App *) appData {return [[self alloc] initWithApp:appData];}// 返回一个不带Model数据的类构造方法+ (instancetype) appView {return [self appViewWithApp:nil];}

 

(5)在Controller中创建”AppView”并加载数据
// 1.创建ViewAppView *appView = [AppView appViewWithApp:appData];// 2.定义每个app的位置、尺寸CGFloat appX = marginX + column * (marginX + APP_WIDTH);CGFloat appY = marginY + row * (marginY + APP_HEIGHT);        appView.frame = CGRectMake(appX, appY, APP_WIDTH, APP_HEIGHT);// 3.加入此app信息到总view        [self.view addSubview:appView];

 

主要代码
ViewController.m
#import "ViewController.h"#import "App.h"#import "AppView.h"#define ICON_KEY @"icon"#define NAME_KEY @"name"#define APP_WIDTH 85#define APP_HEIGHT 90#define MARGIN_HEAD 20#define ICON_WIDTH 50#define ICON_HEIGHT 50#define NAME_WIDTH APP_WIDTH#define NAME_HEIGHT 20#define DOWNLOAD_WIDTH (APP_WIDTH - 20)#define DOWNLOAD_HEIGHT 20@interface ViewController ()/** 存放应用信息 */@property(nonatomic, strong) NSArray *apps; // 应用列表@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.    [self loadApps];}- (void)didReceiveMemoryWarning {    [super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.}#pragma mark 取得应用列表- (NSArray *) apps {if (nil == _apps) {// 1.获得plist的全路径NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];// 2.加载数据NSArray *dictArray  = [NSArray arrayWithContentsOfFile:path];// 3.将dictArray里面的所有字典转成模型,放到新数组中NSMutableArray *appArray = [NSMutableArray array];for (NSDictionary *dict in dictArray) {// 3.1创建模型对象App *app = [App appWithDictionary:dict];// 3.2 添加到app数组中            [appArray addObject:app];        }_apps = appArray;    }return _apps;}#pragma mark 加载全部应用列表- (void) loadApps {int appColumnCount = [self appColumnCount];int appRowCount = [self appRowCount];CGFloat marginX = (self.view.frame.size.width - APP_WIDTH * appColumnCount) / (appColumnCount + 1);CGFloat marginY = (self.view.frame.size.height - APP_HEIGHT * appRowCount) / (appRowCount + 1) + MARGIN_HEAD;int column = 0;int row = 0;for (int index=0; index<self.apps.count; index++) {App *appData =http://www.mamicode.com/ self.apps[index];// 1.创建ViewAppView *appView = [AppView appViewWithApp:appData];// 2.定义每个app的位置、尺寸CGFloat appX = marginX + column * (marginX + APP_WIDTH);CGFloat appY = marginY + row * (marginY + APP_HEIGHT);        appView.frame = CGRectMake(appX, appY, APP_WIDTH, APP_HEIGHT);// 3.加入此app信息到总view        [self.view addSubview:appView];        column++;if (column == appColumnCount) {            column = 0;            row++;        }    }}#pragma mark 计算列数- (int) appColumnCount {int count = 0;    count = self.view.frame.size.width / APP_WIDTH;if ((int)self.view.frame.size.width % (int)APP_WIDTH == 0) {        count--;    }return count;}#pragma mark 计算行数- (int) appRowCount {int count = 0;    count = (self.view.frame.size.height - MARGIN_HEAD) / APP_HEIGHT;if ((int)(self.view.frame.size.height - MARGIN_HEAD) % (int)APP_HEIGHT == 0) {        count--;    }return count;}@end

 

AppView.m:
#import "AppView.h"#import "App.h"// 封装私有属性@interface AppView()// 封装View中的控件,只允许自己访问@property (weak, nonatomic) IBOutlet UIImageView *iconView;@property (weak, nonatomic) IBOutlet UILabel *nameLabel;@end@implementation AppView- (void)setAppData:(App *)appData {// 1.赋值Medel成员_appData =http://www.mamicode.com/ appData;// 2.设置图片self.iconView.image = [UIImage imageNamed:appData.icon];// 3.设置名字self.nameLabel.text = appData.name;}// 自定义将Model数据加载到View的构造方法- (instancetype) initWithApp:(App *) appData {// 1.从NIB取得控件UINib *nib = [UINib nibWithNibName:@"app" bundle:[NSBundle mainBundle]];NSArray *viewArray = [nib instantiateWithOwner:nil options:nil];AppView *appView = [viewArray lastObject];// 2.加载Model    appView.appData =http://www.mamicode.com/ appData;return appView;}// 自定义构造的类方法+ (instancetype) appViewWithApp:(App *) appData {return [[self alloc] initWithApp:appData];}// 返回一个不带Model数据的类构造方法+ (instancetype) appView {return [self appViewWithApp:nil];}@end

 

App.m
#import "App.h"#define ICON_KEY @"icon"#define NAME_KEY @"name"@implementation App- (instancetype) initWithDictionary:(NSDictionary *) dictionary {if (self = [super init]) {self.name = dictionary[NAME_KEY];self.icon = dictionary[ICON_KEY];    }return self;}+ (instancetype) appWithDictionary:(NSDictionary *) dictionary {// 使用self代表类名代替真实类名,防止子类调用出错return [[self alloc] initWithDictionary:dictionary];}@end

[iOS基础控件 - 4.4] 进一步封装"APP列表”,初见MVC模式