[iOS基础控件 - 4.4] 进一步封装"APP列表”,初见MVC模式
A.从ViewController分离View
之前的代码中,View的数据加载逻辑放在了总的ViewController中,增加了耦合性,应该对控制器ViewController隐藏数据加载到View的细节。
封装View的创建逻辑
封装View的数据加载逻辑到自定义的UIView中
B.思路
使用xib封装自定义view的步骤:
1.新建一个继承UIView的自定义view,这里的名字是“AppView”,用来封装独立控件组
每个AppView封装了如下图的控件组
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 ==>
创建声明文件”AppView.h”和“AppView.m”
a.
b.
c.
2.设置xib的class (默认是UIView) 为新建的”AppView"
3.在新建的UIView中编写View的数据加载逻辑
(1)在”AppView.h”中创建Model成员
1 // 在Controller和View之间传输的Model数据 2 @property(nonatomic, strong) App *appData;
(2)连接控件和”AppView",创建私有控件成员
(3)在”AppView.m”中解析加载数据
1 - (void)setAppData:(App *)appData { 2 _appData = appData; 3 4 // 1.设置图片 5 self.iconView.image = [UIImage imageNamed:appData.icon]; 6 // 2.设置名字 7 self.nameLabel.text = appData.name; 8 }
(4)自定义构造方法
AppView.h:
1 // 自定义将Model数据加载到View的构造方法 2 - (instancetype) initWithApp:(App *) appData; 3 // 自定义构造的类方法 4 + (instancetype) appViewWithApp:(App *) appData; 5 // 返回一个不带Model数据的类构造方法 6 + (instancetype) appView;
AppView.m:
1 // 自定义将Model数据加载到View的构造方法 2 - (instancetype) initWithApp:(App *) appData { 3 // 1.从NIB取得控件 4 UINib *nib = [UINib nibWithNibName:@"app" bundle:[NSBundle mainBundle]]; 5 NSArray *viewArray = [nib instantiateWithOwner:nil options:nil]; 6 AppView *appView = [viewArray lastObject]; 7 8 // 2.加载Model 9 appView.appData = appData; 10 11 return appView; 12 } 13 14 // 自定义构造的类方法 15 + (instancetype) appViewWithApp:(App *) appData { 16 return [[self alloc] initWithApp:appData]; 17 } 18 19 // 返回一个不带Model数据的类构造方法 20 + (instancetype) appView { 21 return [self appViewWithApp:nil]; 22 }
(5)在Controller中创建”AppView”并加载数据
1 // 1.创建View 2 AppView *appView = [AppView appViewWithApp:appData]; 3 4 // 2.定义每个app的位置、尺寸 5 CGFloat appX = marginX + column * (marginX + APP_WIDTH); 6 CGFloat appY = marginY + row * (marginY + APP_HEIGHT); 7 appView.frame = CGRectMake(appX, appY, APP_WIDTH, APP_HEIGHT); 8 9 10 // 3.加入此app信息到总view 11 [self.view addSubview:appView];
主要代码
1 ViewController.m 2 #import "ViewController.h" 3 #import "App.h" 4 #import "AppView.h" 5 6 #define ICON_KEY @"icon" 7 #define NAME_KEY @"name" 8 #define APP_WIDTH 85 9 #define APP_HEIGHT 90 10 #define MARGIN_HEAD 20 11 #define ICON_WIDTH 50 12 #define ICON_HEIGHT 50 13 #define NAME_WIDTH APP_WIDTH 14 #define NAME_HEIGHT 20 15 #define DOWNLOAD_WIDTH (APP_WIDTH - 20) 16 #define DOWNLOAD_HEIGHT 20 17 18 @interface ViewController () 19 20 /** 存放应用信息 */ 21 @property(nonatomic, strong) NSArray *apps; // 应用列表 22 23 @end 24 25 @implementation ViewController 26 27 - (void)viewDidLoad { 28 [super viewDidLoad]; 29 // Do any additional setup after loading the view, typically from a nib. 30 31 [self loadApps]; 32 } 33 34 - (void)didReceiveMemoryWarning { 35 [super didReceiveMemoryWarning]; 36 // Dispose of any resources that can be recreated. 37 } 38 39 #pragma mark 取得应用列表 40 - (NSArray *) apps { 41 if (nil == _apps) { 42 // 1.获得plist的全路径 43 NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil]; 44 45 // 2.加载数据 46 NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; 47 48 // 3.将dictArray里面的所有字典转成模型,放到新数组中 49 NSMutableArray *appArray = [NSMutableArray array]; 50 for (NSDictionary *dict in dictArray) { 51 // 3.1创建模型对象 52 App *app = [App appWithDictionary:dict]; 53 54 // 3.2 添加到app数组中 55 [appArray addObject:app]; 56 } 57 58 _apps = appArray; 59 } 60 61 return _apps; 62 } 63 64 #pragma mark 加载全部应用列表 65 - (void) loadApps { 66 int appColumnCount = [self appColumnCount]; 67 int appRowCount = [self appRowCount]; 68 69 CGFloat marginX = (self.view.frame.size.width - APP_WIDTH * appColumnCount) / (appColumnCount + 1); 70 CGFloat marginY = (self.view.frame.size.height - APP_HEIGHT * appRowCount) / (appRowCount + 1) + MARGIN_HEAD; 71 72 int column = 0; 73 int row = 0; 74 for (int index=0; index<self.apps.count; index++) { 75 App *appData = self.apps[index]; 76 77 // 1.创建View 78 AppView *appView = [AppView appViewWithApp:appData]; 79 80 // 2.定义每个app的位置、尺寸 81 CGFloat appX = marginX + column * (marginX + APP_WIDTH); 82 CGFloat appY = marginY + row * (marginY + APP_HEIGHT); 83 appView.frame = CGRectMake(appX, appY, APP_WIDTH, APP_HEIGHT); 84 85 86 // 3.加入此app信息到总view 87 [self.view addSubview:appView]; 88 89 column++; 90 if (column == appColumnCount) { 91 column = 0; 92 row++; 93 } 94 } 95 } 96 97 98 #pragma mark 计算列数 99 - (int) appColumnCount { 100 int count = 0; 101 count = self.view.frame.size.width / APP_WIDTH; 102 103 if ((int)self.view.frame.size.width % (int)APP_WIDTH == 0) { 104 count--; 105 } 106 107 return count; 108 } 109 110 #pragma mark 计算行数 111 - (int) appRowCount { 112 int count = 0; 113 count = (self.view.frame.size.height - MARGIN_HEAD) / APP_HEIGHT; 114 115 if ((int)(self.view.frame.size.height - MARGIN_HEAD) % (int)APP_HEIGHT == 0) { 116 count--; 117 } 118 119 return count; 120 } 121 122 @end
AppView.m:
1 #import "AppView.h" 2 #import "App.h" 3 4 // 封装私有属性 5 @interface AppView() 6 7 // 封装View中的控件,只允许自己访问 8 @property (weak, nonatomic) IBOutlet UIImageView *iconView; 9 @property (weak, nonatomic) IBOutlet UILabel *nameLabel; 10 11 @end 12 13 @implementation AppView 14 15 - (void)setAppData:(App *)appData { 16 // 1.赋值Medel成员 17 _appData = appData; 18 19 // 2.设置图片 20 self.iconView.image = [UIImage imageNamed:appData.icon]; 21 // 3.设置名字 22 self.nameLabel.text = appData.name; 23 } 24 25 // 自定义将Model数据加载到View的构造方法 26 - (instancetype) initWithApp:(App *) appData { 27 // 1.从NIB取得控件 28 UINib *nib = [UINib nibWithNibName:@"app" bundle:[NSBundle mainBundle]]; 29 NSArray *viewArray = [nib instantiateWithOwner:nil options:nil]; 30 AppView *appView = [viewArray lastObject]; 31 32 // 2.加载Model 33 appView.appData = appData; 34 35 return appView; 36 } 37 38 // 自定义构造的类方法 39 + (instancetype) appViewWithApp:(App *) appData { 40 return [[self alloc] initWithApp:appData]; 41 } 42 43 // 返回一个不带Model数据的类构造方法 44 + (instancetype) appView { 45 return [self appViewWithApp:nil]; 46 } 47 48 @end
App.m
1 #import "App.h" 2 3 #define ICON_KEY @"icon" 4 #define NAME_KEY @"name" 5 6 @implementation App 7 8 - (instancetype) initWithDictionary:(NSDictionary *) dictionary { 9 if (self = [super init]) { 10 self.name = dictionary[NAME_KEY]; 11 self.icon = dictionary[ICON_KEY]; 12 } 13 14 return self; 15 } 16 17 18 + (instancetype) appWithDictionary:(NSDictionary *) dictionary { 19 // 使用self代表类名代替真实类名,防止子类调用出错 20 return [[self alloc] initWithDictionary:dictionary]; 21 } 22 23 @end