[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 ==>
创建声明文件”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成员
1 // 在Controller和View之间传输的Model数据
2 @property(nonatomic, strong) App *appData;
 
(2)连接控件和”AppView",创建私有控件成员
Image(10)
 
(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
 
posted @ 2014-11-25 19:42  HelloVoidWorld  阅读(559)  评论(1编辑  收藏  举报