本篇文章,通过字典转模型来改进上篇文章中的代码.
字典转模型,之前的文章已经介绍过,这里再重复一下:
字典转模型
字典:用来存储数据的,用键值对存储数据,是一个nsdictionary ,(不好处:key值容易写错)
模型: 用来存储数据的,一个字典对应一个模型,模型用属性来存储数据,是一个纯洁的object对象
@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *icon;
字典转模型:一个字典对应一个模型,把键值对转化模型的属性,就是把键值对的value赋值给模型的属性
appModel.name = dict[@"name"];
appModel.icon = dict[@"icon"];
属性的类型和名称:属性的类型和键值对的key值的类型是一样,最好名称也和key值的名称一样
* 字典缺陷:
0> 写代码的时候字典的键没有智能提示, 但是模型的属性可以有智能提示
1> "键"是字符串, 如果写错了, 编译器不报错(在编译的时候不报错), 运行时可能出错, 出错了很难找错。
* 把字典转模型的过程封装到"模型"内部
* 原因: 将来的这个"模型"可能会在很多地方被用到(比如有很多个控制器都会使用这个模型), 那么每次用到模型的地方都需要写一次把字典中的数据赋给模型属性的代码, 此时如果把 这些赋值语句封装到模型内部, 会大大简化了使用复杂度与代码量。
*实现思路:
1> 在模型中接收一个NSDictionary的参数, 然后在模型内部把NSDictioanry中的键值对数据赋值给模型的属性。
2> 封装一个initWithDict方法和一个appWithDict方法(规范)
- (instancetype)initWithDict:(NSDictionary *)dict{}
+(instancetype)appModelWithDict:(NSDictionary *)dict{}
应用代码结构及plist截图;
下面附上源代码:
KZAppModel.h
1 // 2 // KZAppModel.h 3 // UI基础-03-05-14 4 // 5 // Created by hukezhu on 15/5/15. 6 // 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface KZAppModel : NSObject 12 /** 13 * 应用图标 14 */ 15 @property (nonatomic ,copy) NSString *icon; 16 /** 17 * 应用名称 18 */ 19 @property (nonatomic ,copy) NSString *name; 20 21 /** 22 * 通过字典来初始化对象 23 * 24 * @param dict 字典对象 25 * 26 * @return 已经初始化完毕的模型对象 27 */ 28 - (instancetype)initWithDict:(NSDictionary *)dict; 29 30 //类方法 31 + (instancetype)appWithModelDict:(NSDictionary *)dict; 32 33 @end
KZAppModel.m
1 // 2 // KZAppModel.m 3 // UI基础-03-05-14 4 // 5 // Created by hukezhu on 15/5/15. 6 // 7 // 8 9 #import "KZAppModel.h" 10 11 @implementation KZAppModel 12 //对象方法 13 -(instancetype)initWithDict:(NSDictionary *)dict{ 14 15 //重写构造方法的默认写法 16 if(self = [super init]){ 17 18 //将字典的所有属性赋值给模型 19 self.icon = dict[@"icon"]; 20 self.name = dict[@"name"]; 21 } 22 return self; 23 } 24 //类方法 25 +(instancetype)appWithModelDict:(NSDictionary *)dict{ 26 27 //注意此处是self 28 return [[self alloc]initWithDict:dict]; 29 } 30 @end
ViewController.m:
1 // 2 // ViewController.m 3 // 03-应用管理 4 // 5 // Created by hukezhu on 15/5/14. 6 // 7 // 8 9 #import "ViewController.h" 10 #import "KZAppModel.h" 11 12 @interface ViewController () 13 @property (nonatomic,strong)NSArray *apps; 14 @end 15 16 @implementation ViewController 17 18 - (void)viewDidLoad { 19 [super viewDidLoad]; 20 21 //每一行的应用的个数 22 int totalCol = 3; 23 24 25 //添加一个小的view 26 CGFloat appW = 80; 27 CGFloat appH = 100; 28 CGFloat marginX = 20; 29 CGFloat marginY = 20; 30 CGFloat hightMargin = 30; 31 CGFloat leftMargin = (self.view.frame.size.width - totalCol * appW - (totalCol - 1) *marginX)* 0.5; 32 33 34 35 for (int i = 0; i < self.apps.count; i++) { 36 37 38 //计算行号和列号 39 int row = i / totalCol; 40 int col = i % totalCol; 41 42 CGFloat appX = leftMargin + (marginX + appW)* col; 43 CGFloat appY = hightMargin + (marginY + appH)* row; 44 45 //1.添加view 46 47 //1.1新建一个UIView 48 UIView *appView = [[UIView alloc]init]; 49 //1.2设置frame 50 appView.frame = CGRectMake(appX, appY, appW, appH); 51 //1.3设置背景色(便于代码阶段验证,之后会删除) 52 //appView.backgroundColor = [UIColor redColor]; 53 //1.4将这个appView添加到view中 54 [self.view addSubview:appView]; 55 56 //加载数据 57 //NSDictionary *dict = self.apps[i]; 58 //将数据赋值给模型对象 59 KZAppModel *appModel = self.apps[i]; 60 61 //2.添加图片UIImageView 62 CGFloat imageW = 60; 63 CGFloat imageH = 50; 64 CGFloat imageX = (appW - imageW)*0.5; 65 CGFloat imageY = 0; 66 UIImageView *imageView = [[UIImageView alloc]init]; 67 imageView.frame = CGRectMake(imageX, imageY, imageW, imageH); 68 //imageView.backgroundColor = [UIColor blueColor]; 69 //imageView.image = [UIImage imageNamed:dict[@"icon"]]; 70 //从模型对象中取出数据 71 imageView.image = [UIImage imageNamed:appModel.icon]; 72 [appView addSubview:imageView]; 73 74 75 //3.添加应用名称 76 77 CGFloat labelW = 80; 78 CGFloat labelH = 25; 79 CGFloat labelX = 0; 80 CGFloat labelY = imageH; 81 UILabel *label = [[UILabel alloc]init]; 82 label.frame = CGRectMake(labelX, labelY, labelW, labelH); 83 //label.backgroundColor = [UIColor grayColor]; 84 //label.text = dict[@"name"]; 85 //从模型对象中取出数据name 86 label.text = appModel.name; 87 88 //设置字体大小 89 label.font = [UIFont systemFontOfSize:13]; 90 //设置字体居中 91 label.textAlignment = NSTextAlignmentCenter; 92 [appView addSubview:label]; 93 94 //4.添加下载按钮 95 96 CGFloat downloadW = 60; 97 CGFloat downloadH = 25; 98 CGFloat downloadX = 10; 99 CGFloat downloadY = labelH + labelY; 100 UIButton *downloadBtn = [[UIButton alloc]init]; 101 downloadBtn.frame = CGRectMake(downloadX, downloadY, downloadW, downloadH); 102 //downloadBtn.backgroundColor = [UIColor yellowColor]; 103 //设置背景图片 104 [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen"] forState:UIControlStateNormal]; 105 [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen_highlighted"] forState:UIControlStateHighlighted]; 106 //设置字体第一种方法 107 [downloadBtn setTitle:@"下载" forState:UIControlStateNormal]; 108 109 //设置字体第二种方法(不推荐使用) 110 downloadBtn.titleLabel.text = @"下载"; 111 112 //设置字体大小 113 downloadBtn.titleLabel.font = [UIFont systemFontOfSize:15]; 114 [appView addSubview:downloadBtn]; 115 116 117 [downloadBtn addTarget:self action:@selector(btnOnClick:) forControlEvents:UIControlEventTouchUpInside]; 118 } 119 120 121 122 123 124 } 125 /** 126 * 按钮的点击方法 127 * 128 * @param btn 将按钮本身传入方法中,哪个按钮被点击就调用这个方法 129 */ 130 - (void)btnOnClick:(UIButton *)btn{ 131 132 //NSLog(@"------%@",btn); 133 btn.enabled = NO; 134 [btn setTitle:@"已下载" forState:UIControlStateNormal]; 135 136 CGFloat labelW = 120; 137 CGFloat labelH = 30; 138 CGFloat labelX = (self.view.frame.size.width - labelW)* 0.5; 139 CGFloat labelY = (self.view.frame.size.height - labelH)*0.5; 140 UILabel *label = [[UILabel alloc]init]; 141 label.frame = CGRectMake(labelX, labelY, labelW, labelH); 142 label.text = @"正在下载"; 143 //设置字体颜色 144 label.textColor = [UIColor redColor]; 145 //设置字体居中 146 label.textAlignment = NSTextAlignmentCenter; 147 //设置 背景色 148 label.backgroundColor = [UIColor blackColor]; 149 150 //设置圆角的半径 151 label.layer.cornerRadius = 8; 152 //将多余的部分减掉 153 label.layer.masksToBounds = YES; 154 //设置透明度 155 label.alpha = 0.0; 156 //将label添加到view中 157 [self.view addSubview:label]; 158 //使用block动画,动画持续时间2秒 159 [UIView animateWithDuration:2.0 animations:^{ 160 label.alpha = 0.5; 161 } completion:^(BOOL finished) { 162 if (finished) { 163 [UIView animateWithDuration:2.0 delay:0.1 options:UIViewAnimationOptionCurveLinear animations:^{ 164 label.alpha = 0.0; 165 } completion:^(BOOL finished) { 166 //上面将透明度设置为0,界面上已经不显示这个label,但是它仍然在内存中,所以为了节约内存,仍要将其从内存中删除 167 [label removeFromSuperview]; 168 169 }]; 170 } 171 }]; 172 173 } 174 /** 175 * "懒加载",加载应用数据 176 * 177 */ 178 - (NSArray *)apps{ 179 180 //如果_apps为空,才加载数据 181 if (_apps == nil) { 182 //获取plist的全路径 183 NSString *path = [[NSBundle mainBundle]pathForResource:@"app.plist" ofType:nil]; 184 185 //加载数组 186 NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; 187 188 //创建一个可变数组,来动态接收模型对象 189 NSMutableArray *array = [NSMutableArray array]; 190 191 //通过循环,将字典数组的字典取出,转成模型对象 192 for (NSDictionary *dict in dictArray) { 193 KZAppModel *appModel = [KZAppModel appWithModelDict:dict]; 194 [array addObject:appModel]; 195 } 196 _apps = array; 197 } 198 return _apps; 199 } 200 201 - (void)didReceiveMemoryWarning { 202 [super didReceiveMemoryWarning]; 203 // Dispose of any resources that can be recreated. 204 } 205 206 @end
相比较上篇文章的代码增加的知识点:
1.字典转模型
文章开头,以及前面的文章介绍了好多次了,这里不再赘述.
2.在模型类中,声明和实现方法的时候,定义的返回值类型为instancetype,这个与id的区别:
1. 使用id作为方法返回值的问题:
在接收方法的返回值的时候可以使用任何类型来接收, 编译都不报错, 但是运行时可能出错。
2. instancetype需要注意的点
1> instancetype在类型表示上, 与id意思一样, 都表示任何对象类型
2> instancetype只能用作返回值类型, 不能向id一样声明变量、用作参数等
3> 使用instancetype, 编译器会检测instancetype的真实类型, 如果类型不匹配, 编译时就发出警告。(instancetype出现在哪个类型中就表示对应的类型)
这里我们还是使用了代码来创建的空间,苹果提供了xib,用来描述局部界面的文件(相对于storyboard的描述全局文件来说),使用Xib我们可以通过拖拽的方法来进行可视化开发,比较方便,下篇文章,将使用xib来改进这个代码.