1 第一节课:
2 1.复习
3 2.运行App应用管理,简单界面分析
4 3.一个应用为一个整体,直接创建一个appView然后计算frame
5 4.说明弊端,应该根据数据的个数来for循环创建
6
7 第二节课:
8 5.加载plist文件字典转模型
9 6.分析计算frame 宽高固定,x,y动态去计算
10 6.1. 行号 row = i / columnCount
11 6.2 列号 col = i % columnCount
12 leftMargin = (屏幕的宽 - (appW * columnCount) - appColMagrin * (columnCount - 1)) * 0.5;
13 appX = leftMargin + (appW + appColMagrin) * col;
14 appY = topMargin + (appY + appRowMagrin) * row
15
16 第三节:
17 6.3创建appView内部三个字控件,iconView, nameLabel , downloadBtn
18 6.4 先创建设置背景色,计算frame添加到appView中
19 6.5 给子控件设置数据,设置label字体和文字对齐方法
20 注意点:给按钮设置图片或文字还有文字颜色时,要用set方法指定不同状态的文字或图片,不能直接访问titleLabel去给按钮设置文字
21 7.分析懒加载代码中的问题封装字典转模型细节,
22
23
24 instancetype 是和id
25 1、相同点
26 都可以作为方法的返回类型
27
28 2、不同点
29 1> instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象;
30
31 2> instancetype只能作为返回值,不能像id那样作为参数,和声明属性
32
33 id是个万能指针编译时不确定真实类型,只有在运行时才知道真实的类型
34 instancetype在编译时就可以确定真实类型,类型一般为所在类的对象类型
35
36 下午:
37
38 第一节课:
39
40 7.1 自定义构造方法,并提供一个类方法
41 7.2 自定义构造方法时的方法命名规范,注意点
42 7.3 代码进一步优化,引出xib
43 8.xib和storyboard的区别及开发中如何去选择
44 9.MVC概念的引入
45 9.1用xib去封装appView,并创建一个继承至UIView的类,和xib的类型关联
46 9.2把xib中的控件连线到自定义类的.h中可以在外面直接访问去给子控件设置数据
47 9.3.弊端,把模型变成属性引入到自定义view类,当控制器为自定义view类的模型属性赋值时会调用模型属性的set方法,如果我们重写了属性得set方法就会调用我们重写得set方法,在此方法中给appView中的子控件设置数据
48
49 第二节课:
50 10.添加下载按钮点击事件
51 10.1 点击下载按钮弹出提示标签,并加入动画
52
53 第三节课:
54 11.回顾用xib自定义view的步骤
55 1.创建一个xib文件,在xib文件中布局好子控件,并设置好对应控件的属性
56 2.创建一个和xib文件相同的类,此类继承至那个类,取决于xib文件中最顶层控件的类型,
57 3.指定xib文件的类型为我们自定义的类,然后把需要修改的控件拖线到所关联类的.m文件中
58 4.自定义类,定义模型属性,并重写模型属性的set方法,在此方法给子控件设置数据
59 5.在自定义类提供一个可供外部访问的类方法,把加载xib创建appView的过程封装到自定义类中
60
61
62
63
64
65
66
67
68 *********** UIViewAnimationOption(动画选项,默认为匀速) ********
69 常规动画属性设置(可以同时选择多个进行设置)
70
71 UIViewAnimationOptionLayoutSubviews:动画过程中保证子视图跟随运动。
72
73 UIViewAnimationOptionAllowUserInteraction:动画过程中允许用户交互。
74
75 UIViewAnimationOptionBeginFromCurrentState:所有视图从当前状态开始运行。
76
77 UIViewAnimationOptionRepeat:重复运行动画。
78
79 UIViewAnimationOptionAutoreverse :动画运行到结束点后仍然以动画方式回到初始点。
80
81 UIViewAnimationOptionOverrideInheritedDuration:忽略嵌套动画时间设置。
82
83 UIViewAnimationOptionOverrideInheritedCurve:忽略嵌套动画速度设置。
84
85 UIViewAnimationOptionAllowAnimatedContent:动画过程中重绘视图(注意仅仅适用于转场动画)。
86
87 UIViewAnimationOptionShowHideTransitionViews:视图切换时直接隐藏旧视图、显示新视图,而不是将旧视图从父视图移除(仅仅适用于转场动画)
88 UIViewAnimationOptionOverrideInheritedOptions :不继承父动画设置或动画类型。
89
90 2.动画速度控制(可从其中选择一个设置)
91
92 UIViewAnimationOptionCurveEaseInOut:动画先缓慢,然后逐渐加速。
93
94 UIViewAnimationOptionCurveEaseIn :动画逐渐变慢。
95
96 UIViewAnimationOptionCurveEaseOut:动画逐渐加速。
97
98 UIViewAnimationOptionCurveLinear :动画匀速执行,默认值。
99
100 3.转场类型(仅适用于转场动画设置,可以从中选择一个进行设置,基本动画、关键帧动画不需要设置)
101
102 UIViewAnimationOptionTransitionNone:没有转场动画效果。
103
104 UIViewAnimationOptionTransitionFlipFromLeft :从左侧翻转效果。
105
106 UIViewAnimationOptionTransitionFlipFromRight:从右侧翻转效果。
107
108 UIViewAnimationOptionTransitionCurlUp:向后翻页的动画过渡效果。
109
110 UIViewAnimationOptionTransitionCurlDown :向前翻页的动画过渡效果。
111
112 UIViewAnimationOptionTransitionCrossDissolve:旧视图溶解消失显示下一个新视图的效果。
113
114 UIViewAnimationOptionTransitionFlipFromTop :从上方翻转效果。
115
116 UIViewAnimationOptionTransitionFlipFromBottom:从底部翻转效果。
一、创建九宫格
实现思路
(1)明确每一块用得是什么view
(2)明确每个view之间的父子关系,每个视图都只有一个父视图,拥有很多的子视图。
(3)可以先尝试逐个的添加格子,最后考虑使用for循环,完成所有uiview的创建
(4)加载app数据,根据数据长度创建对应个数的格子
(5)添加格子内部的子控件
(6)给内部的子控件装配数据
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 设置每行应用的个数
int clumns = 3;
// 获取控制器所管理的view的宽度
CGFloat viewWidth = self.view.frame.size.width;
// 每个应用的宽和高
CGFloat appW = 75;
CGFloat appH = 90;
CGFloat marginTop = 30;//第一行距顶部的距离
CGFloat marginX = (viewWidth - appW*clumns)/(clumns+1);
CGFloat marginY = marginX;//设每行之间的间距与marginX相等
for (int i=0; i<9; i++) {
//1.创建每个应用(UIView)
UIView *appView = [[UIView alloc] init];
//2.设置appView的属性
//2.1设置appView的背景色
appView.backgroundColor = [UIColor orangeColor];
//2.2设置appView的frame属性
//计算每个单元格的列索引
int colIdx = i%clumns;
//计算每个单元格的行索引
int rowIdy = i/clumns;
CGFloat appX = marginX+(appW+marginX)*colIdx;
CGFloat appY = marginTop+(appH+marginY)*rowIdy;
appView.frame = CGRectMake(appX, appY, appW, appH);
//3.将appView加到self.view
[self.view addSubview:appView];
}
}
二、向九宫格内加控件
1 #import "ViewController.h"
2
3 @interface ViewController ()
4 /**
5 * 用来存放所有数据的数组
6 */
7 @property (nonatomic, strong) NSArray *appes;
8 @end
9
10 @implementation ViewController
11
12 /**
13 * 控制器的view加载完成之后就会调用此方法
14 */
15 - (void)viewDidLoad {
16 [super viewDidLoad];
17 // 格子之间的间距
18 CGFloat margin = 20;
19 // 格子的宽
20 CGFloat appViewW = 100;
21 // 格子的高
22 CGFloat appViewH = 120;
23 // 一行中用三个格子
24 NSInteger column = 3;
25 // 最左边的间距 = (控制器view的宽 - 一行中所有格子的宽 - 格子间的间距 * (一行中格子的个数 - 1)) * 0.5
26 CGFloat leftMargin = (self.view.bounds.size.width - appViewW * column - (column - 1) *margin) * 0.5;
27 CGFloat topMargin = leftMargin;
28 // 根据数据的个数来动画创建每一个应用
29 for (NSInteger i = 0; i < self.appes.count; i++) {
30
31 // 0> 先取出每一个应该的数据
32 NSDictionary *dict = self.appes[i];
33
34 // 1.创建appView 用来装里面的子控件
35 UIView *appView = [[UIView alloc] init];
36 // 2.设置背景色
37 // appView.backgroundColor = [UIColor blueColor];
38
39 // 计算列号
40 NSInteger col = i % column;
41 // 计算行号
42 NSInteger row = i / column;
43 // 3.设置frame
44 // 计算appViewX = 左边间距 + (appView宽 + 格子间距) *当前格子是在当前行中的第几个
45 CGFloat appViewX = leftMargin + (appViewW + margin) * col;
46 // appViewY = 顶部间距 + (appViewH + margin) * 当前格子在当前列中是第几个
47 CGFloat appViewY = topMargin + (appViewH + margin) * row;
48
49 appView.frame = CGRectMake(appViewX, appViewY, appViewW, appViewH);
50
51 // 4.添加到控制器的view中
52 [self.view addSubview:appView];
53
54 // 5.创建应用图片
55 UIImageView *iconView = [[UIImageView alloc] init];
56 // 设置背景色
57 // iconView.backgroundColor = [UIColor purpleColor];
58 CGFloat iconW = 70;
59 CGFloat iconH = iconW;
60 CGFloat iconX = (appViewW - iconW) * 0.5;
61 CGFloat iconY = 0;
62 iconView.frame = CGRectMake(iconX, iconY, iconW, iconH);
63 [appView addSubview:iconView];
64
65 // 设置应用图片
66 iconView.image = [UIImage imageNamed:dict[@"icon"]];
67
68 // 6.应用的名称
69 UILabel *nameLabel = [[UILabel alloc] init];
70 // 设置背景色
71 // nameLabel.backgroundColor = [UIColor yellowColor];
72 CGFloat nameX = 0;
73 // CGFloat nameY = iconY + iconH;
74 CGFloat nameY = CGRectGetMaxY(iconView.frame);
75 CGFloat nameW = appViewW;
76 CGFloat nameH = 21;
77 // 设置frame
78 nameLabel.frame = CGRectMake(nameX, nameY, nameW, nameH);
79 // 把名称标签添加到父控件中
80 [appView addSubview:nameLabel];
81
82 // 设置应用名称
83 nameLabel.text = dict[@"name"];
84 // 设置字体大小
85 nameLabel.font = [UIFont systemFontOfSize:13.0];
86 // 设置文字居中
87 nameLabel.textAlignment = NSTextAlignmentCenter;
88
89 // 7.下载按钮
90 UIButton *downloadBtn = [[UIButton alloc] init];
91 // downloadBtn.backgroundColor = [UIColor redColor];
92 CGFloat downloadX = iconX;
93 CGFloat downloadY = CGRectGetMaxY(nameLabel.frame);
94 CGFloat downlaodW = iconW;
95 CGFloat downlaodH = 30;
96 downloadBtn.frame = CGRectMake(downloadX, downloadY, downlaodW, downlaodH);
97 [appView addSubview:downloadBtn];
98
99 // 设置按钮背景图片
100 [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen"] forState:UIControlStateNormal];
101 [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen_highlighted"] forState:UIControlStateDisabled];
102 // 设置按钮的文字
103 [downloadBtn setTitle:@"下载" forState:UIControlStateNormal];
104 [downloadBtn setTitle:@"已下载" forState:UIControlStateDisabled];
105
106 downloadBtn.titleLabel.font = [UIFont systemFontOfSize:15];
107 }
108 }
109
110
111 #pragma mark - 懒加载
112 - (NSArray *)appes {
113 // 1.判断当前的数组属性是否为空,如果为空的时候再去加载数据
114 if (_appes == nil) {
115 // 2.获取plist文件路径
116 NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];
117 // 3.加载plist文件
118 NSArray *dictArr = [NSArray arrayWithContentsOfFile:path];
119 _appes = dictArr;
120
121 }
122
123 return _appes;
124
125 }
126
127 @end
三、向九宫格内加数据
1 #import "ViewController.h"
2
3 @interface ViewController ()
4 /**
5 * 用来存放所有数据的数组
6 */
7 @property (nonatomic, strong) NSArray *appes;
8 @end
9
10 @implementation ViewController
11
12 /**
13 * 控制器的view加载完成之后就会调用此方法
14 */
15 - (void)viewDidLoad {
16 [super viewDidLoad];
17 // 格子之间的间距
18 CGFloat margin = 20;
19 // 格子的宽
20 CGFloat appViewW = 100;
21 // 格子的高
22 CGFloat appViewH = 120;
23 // 一行中用三个格子
24 NSInteger column = 3;
25 // 最左边的间距 = (控制器view的宽 - 一行中所有格子的宽 - 格子间的间距 * (一行中格子的个数 - 1)) * 0.5
26 CGFloat leftMargin = (self.view.bounds.size.width - appViewW * column - (column - 1) *margin) * 0.5;
27 CGFloat topMargin = leftMargin;
28 // 根据数据的个数来动画创建每一个应用
29 for (NSInteger i = 0; i < self.appes.count; i++) {
30
31 // 0> 先取出每一个应该的数据
32 NSDictionary *dict = self.appes[i];
33
34 // 1.创建appView 用来装里面的子控件
35 UIView *appView = [[UIView alloc] init];
36 // 2.设置背景色
37 // appView.backgroundColor = [UIColor blueColor];
38
39 // 计算列号
40 NSInteger col = i % column;
41 // 计算行号
42 NSInteger row = i / column;
43 // 3.设置frame
44 // 计算appViewX = 左边间距 + (appView宽 + 格子间距) *当前格子是在当前行中的第几个
45 CGFloat appViewX = leftMargin + (appViewW + margin) * col;
46 // appViewY = 顶部间距 + (appViewH + margin) * 当前格子在当前列中是第几个
47 CGFloat appViewY = topMargin + (appViewH + margin) * row;
48
49 appView.frame = CGRectMake(appViewX, appViewY, appViewW, appViewH);
50
51 // 4.添加到控制器的view中
52 [self.view addSubview:appView];
53
54 // 5.创建应用图片
55 UIImageView *iconView = [[UIImageView alloc] init];
56 // 设置背景色
57 // iconView.backgroundColor = [UIColor purpleColor];
58 CGFloat iconW = 70;
59 CGFloat iconH = iconW;
60 CGFloat iconX = (appViewW - iconW) * 0.5;
61 CGFloat iconY = 0;
62 iconView.frame = CGRectMake(iconX, iconY, iconW, iconH);
63 [appView addSubview:iconView];
64
65 // 设置应用图片
66 iconView.image = [UIImage imageNamed:dict[@"icon"]];
67
68 // 6.应用的名称
69 UILabel *nameLabel = [[UILabel alloc] init];
70 // 设置背景色
71 // nameLabel.backgroundColor = [UIColor yellowColor];
72 CGFloat nameX = 0;
73 // CGFloat nameY = iconY + iconH;
74 CGFloat nameY = CGRectGetMaxY(iconView.frame);
75 CGFloat nameW = appViewW;
76 CGFloat nameH = 21;
77 // 设置frame
78 nameLabel.frame = CGRectMake(nameX, nameY, nameW, nameH);
79 // 把名称标签添加到父控件中
80 [appView addSubview:nameLabel];
81
82 // 设置应用名称
83 nameLabel.text = dict[@"name"];
84 // 设置字体大小
85 nameLabel.font = [UIFont systemFontOfSize:13.0];
86 // 设置文字居中
87 nameLabel.textAlignment = NSTextAlignmentCenter;
88
89 // 7.下载按钮
90 UIButton *downloadBtn = [[UIButton alloc] init];
91 // downloadBtn.backgroundColor = [UIColor redColor];
92 CGFloat downloadX = iconX;
93 CGFloat downloadY = CGRectGetMaxY(nameLabel.frame);
94 CGFloat downlaodW = iconW;
95 CGFloat downlaodH = 30;
96 downloadBtn.frame = CGRectMake(downloadX, downloadY, downlaodW, downlaodH);
97 [appView addSubview:downloadBtn];
98
99 // 设置按钮背景图片
100 [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen"] forState:UIControlStateNormal];
101 [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen_highlighted"] forState:UIControlStateDisabled];
102 // 设置按钮的文字
103 [downloadBtn setTitle:@"下载" forState:UIControlStateNormal];
104 [downloadBtn setTitle:@"已下载" forState:UIControlStateDisabled];
105
106 downloadBtn.titleLabel.font = [UIFont systemFontOfSize:15];
107 }
108 }
109
110
111 #pragma mark - 懒加载
112 - (NSArray *)appes {
113 // 1.判断当前的数组属性是否为空,如果为空的时候再去加载数据
114 if (_appes == nil) {
115 // 2.获取plist文件路径
116 NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];
117 // 3.加载plist文件
118 NSArray *dictArr = [NSArray arrayWithContentsOfFile:path];
119 _appes = dictArr;
120
121 }
122
123 return _appes;
124
125 }
代码问题
我们是直接通过字典的键名获取plist中的数据信息,在viewController中需要直接和数据打交道,如果需要多次使用可能会因为不小心把键名写错,而程序并不报错。鉴于此,可以考虑把字典数据转换成一个模型,把数据封装到一个模型中去,让viewController不再直接和数据打交道,而是和模型交互。
一般情况下,设置数据和取出数据都使用“字符串类型的key”,编写这些key时,编辑器没有智能提示,需要手敲。如:
dict[@"name"] = @"Jack";
NSString *name = dict[@"name"];
手敲字符串key,key容易写错
Key如果写错了,编译器不会有任何警告和报错,造成设错数据或者取错数据
四、字典转模型
字典转模型的好处:
(1)降低代码的耦合度
(2)所有字典转模型部分的代码统一集中在一处处理,降低代码出错的几率
(3)在程序中直接使用模型的属性操作,提高编码效率
(4)调用方不用关心模型内部的任何处理细节
字典转模型的注意点:
模型应该提供一个可以传入字典参数的构造方法
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)xxxWithDict:(NSDictionary *)dict;
提示:在模型中合理地使用只读属性,可以进一步降低代码的耦合度。
#import <Foundation/Foundation.h>
/** NSString block用copy
除了字符串和 block 控件 其它基本OC对象都用strong
weak 控件最好都用
assgin 基本数据类型
*/
@interface HMApp : NSObject
/** 应用图片 */
@property (nonatomic, copy) NSString *icon;
/** 应用名称 */
@property (nonatomic, copy) NSString *name;
// id他instancetype有什么区别
// 1.相同点:他们都可以当方法的返回值
// 2.不同点:id可以来声明变量,id可以当参数类型,\
// id是一个万能指针,他在编译的时候不会确定真实的类型,只有在运行时候才知道真实类型
// instancetype在编译的时候就能确定他的返回值真实类型,这种更安全
//clang 3.5
/** */
- (instancetype)initWithDict:(NSDictionary *)dict;//用字典实例化对象的成员方法
+ (instancetype)appWithDict:(NSDictionary *)dict;//用字典实例化对象的类方法,又称工厂方法
@end
#import "HMApp.h"
@implementation HMApp
- (instancetype)initWithDict:(NSDictionary *)dict {
if (self = [super init]) {
self.icon = dict[@"icon"];
self.name = dict[@"name"];
[self setValuesForKeysWithDictionary:dict];//加载所有属性
}
return self;
}
+ (instancetype)appWithDict:(NSDictionary *)dict {
return [[self alloc] initWithDict:dict];
}
@end
/**
xib和stroyboard的区别
1.相同点:都可以用来我们软件界面
2.不同点:在storyboard可以用来搭建整个应用的所有界面,最少也是一个界面
xib可以用来描述一个界面中的某一个部分,或一个应用的一个界面,及整个界面
*/
#import "ViewController.h"
#import "HMApp.h"
@interface ViewController ()
/**
* 用来存放所有数据的数组
*/
@property (nonatomic, strong) NSArray *appes;
@end
@implementation ViewController
/**
* 控制器的view加载完成之后就会调用此方法
*/
- (void)viewDidLoad {
[super viewDidLoad];
// 格子之间的间距
CGFloat margin = 20;
// 格子的宽
CGFloat appViewW = 100;
// 格子的高
CGFloat appViewH = 120;
// 一行中用三个格子
NSInteger column = 3;
// 最左边的间距 = (控制器view的宽 - 一行中所有格子的宽 - 格子间的间距 * (一行中格子的个数 - 1)) * 0.5
CGFloat leftMargin = (self.view.bounds.size.width - appViewW * column - (column - 1) *margin) * 0.5;
CGFloat topMargin = leftMargin;
// 根据数据的个数来动画创建每一个应用
for (NSInteger i = 0; i < self.appes.count; i++) {
// 0> 先取出每一个应该的数据
// NSDictionary *dict = self.appes[i];
// 取数组中每一个用来表示数据的模型对象
HMApp *app = self.appes[i];
// 1.创建appView 用来装里面的子控件
UIView *appView = [[UIView alloc] init];
// 2.设置背景色
// appView.backgroundColor = [UIColor blueColor];
// 计算列号
NSInteger col = i % column;
// 计算行号
NSInteger row = i / column;
// 3.设置frame
// 计算appViewX = 左边间距 + (appView宽 + 格子间距) *当前格子是在当前行中的第几个
CGFloat appViewX = leftMargin + (appViewW + margin) * col;
// appViewY = 顶部间距 + (appViewH + margin) * 当前格子在当前列中是第几个
CGFloat appViewY = topMargin + (appViewH + margin) * row;
appView.frame = CGRectMake(appViewX, appViewY, appViewW, appViewH);
// 4.添加到控制器的view中
[self.view addSubview:appView];
// 5.创建应用图片
UIImageView *iconView = [[UIImageView alloc] init];
// 设置背景色
// iconView.backgroundColor = [UIColor purpleColor];
CGFloat iconW = 70;
CGFloat iconH = iconW;
CGFloat iconX = (appViewW - iconW) * 0.5;
CGFloat iconY = 0;
iconView.frame = CGRectMake(iconX, iconY, iconW, iconH);
[appView addSubview:iconView];
// 设置应用图片
iconView.image = [UIImage imageNamed:app.icon];
// 6.应用的名称
UILabel *nameLabel = [[UILabel alloc] init];
// 设置背景色
// nameLabel.backgroundColor = [UIColor yellowColor];
CGFloat nameX = 0;
// CGFloat nameY = iconY + iconH;
CGFloat nameY = CGRectGetMaxY(iconView.frame);
CGFloat nameW = appViewW;
CGFloat nameH = 21;
// 设置frame
nameLabel.frame = CGRectMake(nameX, nameY, nameW, nameH);
// 把名称标签添加到父控件中
[appView addSubview:nameLabel];
// 设置应用名称
nameLabel.text = app.name;
// 设置字体大小
nameLabel.font = [UIFont systemFontOfSize:13.0];
// 设置文字居中
nameLabel.textAlignment = NSTextAlignmentCenter;
// 7.下载按钮
UIButton *downloadBtn = [[UIButton alloc] init];
// downloadBtn.backgroundColor = [UIColor redColor];
CGFloat downloadX = iconX;
CGFloat downloadY = CGRectGetMaxY(nameLabel.frame);
CGFloat downlaodW = iconW;
CGFloat downlaodH = 30;
downloadBtn.frame = CGRectMake(downloadX, downloadY, downlaodW, downlaodH);
[appView addSubview:downloadBtn];
// 设置按钮背景图片
[downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen"] forState:UIControlStateNormal];
[downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen_highlighted"] forState:UIControlStateDisabled];
// 设置按钮的文字
[downloadBtn setTitle:@"下载" forState:UIControlStateNormal];
[downloadBtn setTitle:@"已下载" forState:UIControlStateDisabled];
downloadBtn.titleLabel.font = [UIFont systemFontOfSize:15];
}
}
#pragma mark - 懒加载
- (NSArray *)appes {
// 1.判断当前的数组属性是否为空,如果为空的时候再去加载数据
if (_appes == nil) {
// 2.获取plist文件路径
NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];
// 3.加载plist文件
NSArray *dictArr = [NSArray arrayWithContentsOfFile:path];
// 创建一个可变数据用来保存每一个模型对象(创建可变数组时并给其分配好容量)
NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:dictArr.count];
// 遍历字典数组把每一个字典转换成一个模型对象
for (NSDictionary *dict in dictArr) {
// 创建模型对象
// HMApp *app = [[HMApp alloc] initWithDict:dict];
HMApp *app = [HMApp appWithDict:dict];
// 给模型对象中的属性赋值
// app.icon = dict[@"icon"];
// app.name = dict[@"name"];
// 把模型添加到数组
[arrM addObject:app];
}
// 把装有所有模型的数组赋值给我们的数组属性
_appes = arrM;
}
return _appes;
}
@end
五、用xib自定义view
HMAppView.xib文件
1 /*--------------------Model-------------------*/
2
3 #import <Foundation/Foundation.h>
4 /** NSString block用copy
5 除了字符串和 block 控件 其它基本OC对象都用strong
6 weak 控件最好都用
7 assgin 基本数据类型
8 */
9 @interface HMApp : NSObject
10 /** 应用图片 */
11 @property (nonatomic, copy) NSString *icon;
12 /** 应用名称 */
13 @property (nonatomic, copy) NSString *name;
14
15 // id他instancetype有什么区别
16 // 1.相同点:他们都可以当方法的返回值
17 // 2.不同点:id可以来声明变量,id可以当参数类型,\
18 // id是一个万能指针,他在编译的时候不会确定真实的类型,只有在运行时候才知道真实类型
19 // instancetype在编译的时候就能确定他的返回值真实类型,这种更安全
20 //clang 3.5
21 - (instancetype)initWithDict:(NSDictionary *)dict;
22 + (instancetype)appWithDict:(NSDictionary *)dict;
23 @end
24
25
26
27 #import "HMApp.h"
28
29 @implementation HMApp
30 - (instancetype)initWithDict:(NSDictionary *)dict {
31 if (self = [super init]) {
32 self.icon = dict[@"icon"];
33 self.name = dict[@"name"];
34 }
35
36 return self;
37 }
38
39 + (instancetype)appWithDict:(NSDictionary *)dict {
40 return [[self alloc] initWithDict:dict];
41 }
42 @end
43
44 /*-----------------View---------------------*/
45 #import <UIKit/UIKit.h>
46 @class HMApp;
47 @interface HMAppView : UIView
48 // 把模型变成一个属性引用到自定义视图中
49 @property (nonatomic, strong) HMApp *app;
50
51 + (instancetype)appView;
52 @end
53
54
55
56 #import "HMAppView.h"
57 #import "HMApp.h"
58
59 @interface HMAppView ()
60 /** 应用的图片 */
61 @property (weak, nonatomic) IBOutlet UIImageView *iconView;
62 /** 应用的名称 */
63 @property (weak, nonatomic) IBOutlet UILabel *nameLabel;
64 @end
65
66 @implementation HMAppView
67 // 当点击下载按钮之后调用的方法
68 - (IBAction)downloadBtnClick:(UIButton *)downBtn {
69 NSLog(@"%ld----%@", self.tag ,self);
70 // 1.把按钮设置为禁用状态
71 downBtn.enabled = NO;
72 // 2.创建一个label
73 UILabel *downLabel = [[UILabel alloc] init];
74 downLabel.text = [NSString stringWithFormat:@"%@下载完成", self.app.name];
75 // 设置字体
76 downLabel.font = [UIFont systemFontOfSize:13.0];
77 // 设置文字居中
78 downLabel.textAlignment = NSTextAlignmentCenter;
79 // 设置label的文字颜色
80 downLabel.textColor = [UIColor redColor];
81 // 设置背景色
82 downLabel.backgroundColor = [UIColor blackColor];
83 // 设置bounds
84 downLabel.bounds = CGRectMake(0, 0, 200, 21);
85 // 设置标签的位置在屏幕的中心
86 downLabel.center = self.superview.center;
87 // 把提示标签添加在控制器的view上
88 [self.superview addSubview:downLabel];
89 // 设置label透明度
90 downLabel.alpha = 0.0;
91 // 设置圆角半径
92 downLabel.layer.cornerRadius = 5;
93 // 把超出边界的部分裁剪掉
94 downLabel.clipsToBounds = YES;
95 // downLabel.layer.masksToBounds = YES;
96
97 //执行一个两秒中的动画,让标签慢慢的显示
98 [UIView animateWithDuration:2.0 animations:^{ // 表示要执行的动画代码
99 // 设置label的透明度
100 downLabel.alpha = 0.7;
101 } completion:^(BOOL finished) { // 表示动画执行完成之后要做得事情
102 // 上面的动画执行完成之后推迟2秒之后执行一个2秒的一个动画
103 [UIView animateWithDuration:2.0 delay:2.0 options:UIViewAnimationOptionCurveLinear animations:^{
104 // 设置label透明度
105 downLabel.alpha = 0.0;
106 } completion:^(BOOL finished) { // 动画执行完成之后把label从父控件中移除
107 // 把这个label从它的父控件中移除掉
108 [downLabel removeFromSuperview];
109 }];
110 }];
111 }
112
113
114 + (instancetype)appView {
115 return [[[NSBundle mainBundle] loadNibNamed:@"HMAppView" owner:nil options:nil]lastObject];
116 }
117 // 重写模型属性的set方法,当外部给自定义视图的模型属性赋值时就会调用此方法,
118 - (void)setApp:(HMApp *)app {
119 #warning mark - 重写set方法一定要注意给属性下划线的成员变量赋值
120 _app = app;
121 // 1.设置应用图片
122 self.iconView.image = [UIImage imageNamed:app.icon];
123 // 2.设置应用名称
124 self.nameLabel.text = app.name;
125
126
127 }
128
129
130 @end
131
132 /*-------------------Controller--------------*/
133 /**
134 xib和stroyboard的区别
135 1.相同点:都可以用来我们软件界面
136 2.不同点:在storyboard可以用来搭建整个应用的所有界面,最少也是一个界面
137 xib可以用来描述一个界面中的某一个部分,或一个应用的一个界面,及整个界面
138 */
139 #import "ViewController.h"
140 #import "HMApp.h"
141 #import "HMAppView.h"
142
143 @interface ViewController ()
144 /**
145 * 用来存放所有数据的数组
146 */
147 @property (nonatomic, strong) NSArray *appes;
148 @end
149
150 @implementation ViewController
151
152 /**
153 * 控制器的view加载完成之后就会调用此方法
154 */
155 - (void)viewDidLoad {
156 [super viewDidLoad];
157 // 格子之间的间距
158 CGFloat margin = 20;
159 // // 格子的宽
160 // CGFloat appViewW = 100;
161 // // 格子的高
162 // CGFloat appViewH = 120;
163 // 一行中用三个格子
164 NSInteger column = 3;
165 // 根据数据的个数来动画创建每一个应用
166 for (NSInteger i = 0; i < self.appes.count; i++) {
167
168 // 1.创建appView
169 HMAppView *appView = [HMAppView appView];
170 appView.tag = i;
171 // 2.取数组中每一个用来表示数据的模型对象
172 HMApp *app = self.appes[i];
173 // 3.给自定义视图传递模型
174 appView.app = app;
175 // 自定义视图的大小应该根据xib中的视图的大小来动态的设置而不应该直接固定死
176 // 4.拿到xib中appView的宽
177 CGFloat appViewW = appView.frame.size.width;
178 // xib中appView的高来表示我们一个视图的高
179 CGFloat appViewH = appView.frame.size.height;
180 // 最左边的间距 = (控制器view的宽 - 一行中所有格子的宽 - 格子间的间距 * (一行中格子的个数 - 1)) * 0.5
181 CGFloat leftMargin = (self.view.bounds.size.width - appViewW * column - (column - 1) *margin) * 0.5;
182 // 头部间距
183 CGFloat topMargin = leftMargin;
184 // 计算列号
185 NSInteger col = i % column;
186 // 计算行号
187 NSInteger row = i / column;
188 // 3.设置frame
189 // 计算appViewX = 左边间距 + (appView宽 + 格子间距) *当前格子是在当前行中的第几个
190 CGFloat appViewX = leftMargin + (appViewW + margin) * col;
191 // appViewY = 顶部间距 + (appViewH + margin) * 当前格子在当前列中是第几个
192 CGFloat appViewY = topMargin + (appViewH + margin) * row;
193
194 appView.frame = CGRectMake(appViewX, appViewY, appViewW, appViewH);
195
196 // 4.添加到控制器的view中
197 [self.view addSubview:appView];
198
199 }
200 }
201
202
203 #pragma mark - 懒加载
204 - (NSArray *)appes {
205 // 1.判断当前的数组属性是否为空,如果为空的时候再去加载数据
206 if (_appes == nil) {
207 // 2.获取plist文件路径
208 NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];
209 // 3.加载plist文件
210 NSArray *dictArr = [NSArray arrayWithContentsOfFile:path];
211
212 // 创建一个可变数据用来保存每一个模型对象(创建可变数组时并给其分配好容量)
213 NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:dictArr.count];
214 // 遍历字典数组把每一个字典转换成一个模型对象
215 for (NSDictionary *dict in dictArr) {
216 // 创建模型对象
217 // HMApp *app = [[HMApp alloc] initWithDict:dict];
218 HMApp *app = [HMApp appWithDict:dict];
219 // 给模型对象中的属性赋值
220 // app.icon = dict[@"icon"];
221 // app.name = dict[@"name"];
222 // 把模型添加到数组
223 [arrM addObject:app];
224 }
225 // 把装有所有模型的数组赋值给我们的数组属性
226 _appes = arrM;
227
228 }
229
230 return _appes;
231
232 }
233
234
235 /** 用xib来自定义一个视图的步骤
236 1.创建一个xib文件用来描述我们局部界面(并在里面摆放好所有的子控件,并设置好它们的属性)
237 2.创建一个类来和我们的xib文件进行关联(这个类也是用来管理我们的xib文件,创建的自定义类的类名最好和我们xib的文件名称一样)(创建的类它要继承至什么类,取决于xib文件中最顶层控件的类型)
238 3.指定xib中class类型,如果不指定创建出来的xib和我们的自定义类没有任何关系
239 4.把需要修改或需要设置的控件连线到我们自定义类.m中
240 5.在自定义view类的.h文件引入模型,就是把模型当成一个属性定义在我们的自定义view类中
241 6.重写模型属性set方法,当外部给这个模型属性赋值的时候就会调用模型属性的set方法,我们在重写的模型属性的set方法中给自定义视图的子控件去设置数据
242
243 */
244
245 @end
补充说明
View的封装思路
(1) 如果一个view内部的子控件比较多,一般会考虑自定义一个view,把它内部子控件的创建屏蔽起来,不让外界关心
(2) 外界可以传入对应的模型数据给view,view拿到模型数据后给内部的子控件设置对应的数据
mvc机制简单说明
说明:
(1)在开发过程中,作为控制器处理的量级应该很轻,不该操心的不操心。协调好模型和视图就ok了,要学会当一个好老板。
(2)三个部分各司其职,数据模型只负责数据的处理,视图部分只负责把拿到的数据进行显示,两个部分都是被动的,等待着大管家控制器的调遣。
(3)在OC中,如果视图和数据模型之间有通道,那控制器是否处于失控状态呢?
1.MVC是一种设计思想,贯穿于整个iOS开发中,需要积累一定的项目经验,才能深刻体会其中的含义和好处
MVC中的三个角色
M:Model,模型数据
V:View,视图(界面)
C:Control,控制中心
2.MVC的几个明显的特征和体现:
View上面显示什么东西,取决于Model
只要Model数据改了,View的显示状态会跟着更改
Control负责初始化Model,并将Model传递给View去解析展示