您的位置:首页 > 移动开发 > IOS开发

模型的应用 && 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,然后提⽰示会慢慢消失



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios 模型 代理模式 Xib