模型的应用 && Xib &&代理模式的应用【应用管理】
2015-12-10 17:11
211 查看
模型
模型取代字典的好处
使⽤用字典的坏处⼀一般情况下,设置数据和取出数据都使⽤用“字符串类型的key”,编写这些key时,编译器不会有任何友善提示,需要手敲。例如:
dict[@"name"] = @"Jack";
NSString *name = dict[@"name"];
手敲字符串key,key容易写错 。Key如果写错了,编译器不会有任何警告和报错,造成设错数据或者取错数据。
使⽤用模型的好处
所谓模型,其实就是数据模型,专门⽤用来存放数据的对象,⽤用它来表⽰示数据会更加专业。
模型设置数据和取出数据都是通过它的属性,属性名如果写错了,编译器会马上报错,因此,保证了数据的正确性。
使⽤模型访问属性时,编译器会提供一系列的提⽰,提⾼编码效率。例如:
app.name = @"Jack”;
NSString *name = app.name;
字典转模型
字典转模型的过程最好封装在模型内部模型应该提供⼀一个可以传⼊入字典参数的构造⽅方法
-(instancetype)initWithDict:(NSDictionary*)dict;
+(instancetype)xxxWithDict:(NSDictionary*)dict;
instancetype在类型表⽰上,跟id一样,可以表示任何对象类型
instancetype只能用在返回值类型上,不能像id一样用在参数类型上
instancetype比id多一个好处:编译器会检测instancetype的真实类型
构建一个模型类 App 来存储数据
// App.h // 模型类:用来存放数据的类 #import <Foundation/Foundation.h> @interface App : NSObject // 模型的名称 @property (nonatomic, copy) NSString *name; // 模型的图标 @property (nonatomic, copy) NSString *icon; // 通过字典来初始化模型对象 - (instancetype) initWithDict:(NSDictionary *)dict; + (instancetype) appWithDict:(NSDictionary *)dict; @end
// App.m #import "App.h" @implementation App - (instancetype)initWithDict:(NSDictionary *)dict { if(self = [super init]){ self.name = dict[@"name"]; self.icon = dict[@"icon"]; } return self; } + (instancetype)appWithDict:(NSDictionary *)dict { return [[self alloc] initWithDict:dict]; } @end
字典转模型的过程
Xib文件
Xib的创建及使用
Xib文件可以用来描述某一块局部的UI界⾯如何创建Xib文件
首先创建一个继承UIView的自定义view。假如这个类叫:APPView。
新建一个AppView.xib文件来描述AppView内部的结构。修改UIView的类型为AppView真实类型,即把AppView.xib文件的class 改成 AppView如下图所示:
Xib文件的加载
调用方法
NSArray *objs = [[NSBundle mainBundle] loadNibNamed:@"AppView" owner:nil options:nil];
这个方法会创建xib中的所有对象,并且将对象按顺序放到objs数组中。
如果xib如下图所⽰,那么objs数组中依次会有3个对象:1个UIView、1个UILabel、1个UIButton
在开发阶段,面向开发者的是xib文件; 当把应用装到⼿机上时,xib⽂件就会转为nib⽂件
使用xib封装一个自定义view的步骤
1> 新建一个继承UIView的自定义view,假设类名叫做(AppView)2> 新建一个AppView.xib文件来描述MJAppView内部的结构
3> 修改UIView的类型为AppView真实类型
4> 将内部的子控件跟AppView进行属性连线
5> AppView中创建一个模型属性
6> 重写模型属性的set方法,因为在set方法中可以拿到外界传递的模型数据
7> 把模型数据拆开,分别设置数据到对应的子控件中
8> 补充:提供一个创建MJAppView的类方法,将读取xib文件的代码屏蔽起来
Xib和storyboard对比
共同点:都用来描述软件界面
都用Interface Builder⼯具来编辑
不同点
Xib是轻量级的,用来描述局部的UI界面
Storyboard是重量级的,用来描述整个软件的多个界面,并且能展⽰多个界面之间的跳转关系
源码
结构框架
代码
// ViewController.h // #import <UIKit/UIKit.h> @interface ViewController : UIViewController @end
// // ViewController.m // #import "ViewController.h" #import "AppView.h" #import "App.h" @interface ViewController () <AppViewDelegate> @property (nonatomic, strong) NSArray *apps; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 添加应用信息 // 1.总列数(一行最多3列) int totalColumns = 3; // 2.应用的尺寸 CGFloat appW = 90; CGFloat appH = 90; // 3.间隙 = (控制器view的宽度 - 3 * 应用宽度) / 4 CGFloat marginX = (self.view.frame.size.width - totalColumns * appW) / (totalColumns + 1); CGFloat marginY = 40; // 4.根据应用个数创建对应的框框(index 0 ~ 11) for(int index = 0 ; index < self.apps.count; index++){ // 3.1 创建view,设置数据 AppView *appView = [AppView appViewWithApp:self.apps[index]]; // 3.2 把view的代理设置为控制器 appView.delegate = self; // 3.3 添加view [self.view addSubview:appView]; // 3.4 设置frame int row = index / totalColumns; int col = index % totalColumns; // 计算x和y CGFloat appX = marginX + col * (appW + marginX); CGFloat appY = 40 + row * (appH + marginY); appView.frame = CGRectMake(appX, appY, appW, appH); } } // 点击下载按钮时就会调用 - (void)appViewClickedDownloadButton:(AppView *)appView{ // 1.取出模型 App *app = appView.app; // 添加标签 UILabel *tipLabel = [[UILabel alloc] init]; tipLabel.text = [NSString stringWithFormat:@"下载成功%@", app.name]; tipLabel.font = [UIFont systemFontOfSize:14]; tipLabel.textAlignment = NSTextAlignmentCenter; tipLabel.textColor = [UIColor whiteColor]; tipLabel.backgroundColor = [UIColor blackColor]; tipLabel.frame = CGRectMake(0, 0, 170, 40); tipLabel.center = CGPointMake(187.5, 300); tipLabel.alpha = 0.0; tipLabel.layer.cornerRadius = 5; tipLabel.clipsToBounds = YES; [self.view addSubview:tipLabel]; // 3.动画 [UIView animateWithDuration:1.0 animations:^{ tipLabel.alpha = 0.5; } completion:^(BOOL finished) { [UIView animateWithDuration:1.0 delay:1.0 options:UIViewAnimationOptionCurveLinear animations:^{ tipLabel.alpha = 0.0; } completion:^(BOOL finished) { [tipLabel removeFromSuperview]; }]; }]; } - (NSArray *)apps { if(_apps == nil){ // 初始化 // 1.获得plist的全路径 NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil]; // 2.加载数组 NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; // 3.将dictArray里面的所有字典转成模型对象,放到新的数组中 NSMutableArray *appArray = [NSMutableArray array]; for(NSDictionary *dict in dictArray){ // 3.1 创建模型对象 App *app = [App appWithDict:dict]; // 3.2 添加模型对象到数组中 [appArray addObject:app]; } // 4.赋值 _apps = appArray; } return _apps; } @end
// // App.h // 模型类:用来存放数据的类 #import <Foundation/Foundation.h> @interface App : NSObject // 模型的名称 @property (nonatomic, copy) NSString *name; // 模型的图标 @property (nonatomic, copy) NSString *icon; // 通过字典来初始化模型对象 - (instancetype) initWithDict:(NSDictionary *)dict; + (instancetype) appWithDict:(NSDictionary *)dict; @end
// // App.m // #import "App.h" @implementation App - (instancetype)initWithDict:(NSDictionary *)dict { if(self = [super init]){ self.name = dict[@"name"]; self.icon = dict[@"icon"]; } return self; } + (instancetype)appWithDict:(NSDictionary *)dict { return [[self alloc] initWithDict:dict]; } @end
// // AppView.h // #import <UIKit/UIKit.h> @class App, AppView; // 声明一个协议 @protocol AppViewDelegate <NSObject> @optional - (void)appViewClickedDownloadButton:(AppView *)appView; @end @interface AppView : UIView // 代理 @property (nonatomic, weak) id<AppViewDelegate> delegate; // 数据模型 @property (nonatomic, strong) App *app; // 创建模型 + (instancetype)appView; //通过模型数据来创建一个view + (instancetype)appViewWithApp:(App *)app; @end
// // AppView.m // #import "AppView.h" #import "App.h" @interface AppView() @property (weak, nonatomic) IBOutlet UIImageView *iconView; @property (weak, nonatomic) IBOutlet UILabel *nameLabel; - (IBAction)download:(UIButton *)sender; @end @implementation AppView + (instancetype)appViewWithApp:(App *)app{ NSBundle *bundle = [NSBundle mainBundle]; // 读取xib文件(会创建xib中的描述的所有对象,并且按顺序放到数组中返回) NSArray *objs = [bundle loadNibNamed:@"AppView" owner:nil options:nil]; AppView *appView = [objs lastObject]; appView.app = app; return appView; } + (instancetype)appView{ return [self appViewWithApp:nil]; } - (void)setApp:(App *)app { _app = app; // 1.设置图标 self.iconView.image = [UIImage imageNamed:app.icon]; // 2.设置名称 self.nameLabel.text = app.name; } // 下载按钮 - (IBAction)download:(UIButton *)sender { // 1.让按钮失效(文字变为“已下载”) sender.enabled = NO; [sender setTitle:@"已下载" forState:UIControlStateDisabled]; // 2.通知代理,download按钮被点击。 if([self.delegate respondsToSelector:@selector(appViewClickedDownloadButton:)]){ [self.delegate appViewClickedDownloadButton:self]; } } @end
代理模式
为什么要用代理模式
我们要拿到控制器View,然后让它显示tipLabel,但是怎么拿到控制器View?(1)将控制器View传到AppView.m里面,然后在download:方法调用,但这种方法非常不好,耦合性太强。
(2) 将按钮抛出去(放在AppView.h里面,这样控制器也可以监听),让控制器View监听,这样也不好。
正确的方法是用代理模式,控制器作为AppView的代理。
对代理模式的分析
当点击下载按钮的时候,应该通知控制器,控制器得知点击了下载按钮,就执行:添加tipLabel到控制器的View该做的事情应该交给该做的那个类去做(比较适合谁做的事情,就交给谁去做)。比如说我们要拿到控制器的view,添加一个label,谁能直接拿到控制器的view,就是控制器。所以我们得到一个结论,拿到控制器,添加lable的代码(操作),应该放到控制器里面。
控制器要监听【AppView】内部下载按钮的点击,当下载按钮被点击了,会到download:方法,我们要在download:方法里面通知控制器(代理)(
[self.delegate appViewClickedDownloadButton:self];这个方法就是通知代理),下载按钮被点击,然后控制器把label添加到它的view上面上去。
达到效果
点击下载按钮后变成不能点击的“已安装”中间慢慢弹出提⽰示:已经成功安装xxx,然后提⽰示会慢慢消失
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- JavaScript对象模型-执行模型
- C++中事件机制的简洁实现及需要放弃的特性
- 讲解iOS开发中基本的定位功能实现
- js判断客户端是iOS还是Android等移动终端的方法
- IOS开发环境windows化攻略
- 浅析iOS应用开发中线程间的通信与线程安全问题
- Python设计模式之代理模式实例
- 第二节 对象模型 [2]
- 检测iOS设备是否越狱的方法
- .net平台推送ios消息的实现方法
- ASP.NET中实现Form表单字段值自动填充到操作模型中
- Java线程模型缺陷