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中,如果视图和数据模型之间有通道,那控制器是否处于失控状态呢?