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

九宫格之模型,封装初体验

2015-10-01 16:42 573 查看
曾经以为面向对象开发,无非就是封装成类嘛,没什么不同,但是今天在做这个小程序的时候,稍微体会到了一点所谓的,面向对象开发.

以前写C的程序,往往是顺序而过程的编程.像融入到代码中一样,不需要太多的思想.

然而在面向对象编程的时候,需要站在上帝视角上,俯瞰整个项目,着手一个对象模型的构造,在不断的构造对象的时候,实现封装.

===============

用纯代码的方式,实现这样一个小程序



第一步,图标放到Images.xcassets文件中就行了, 因为文件不是太大,不会影响内存的使用, 然后将图片的描述信息,放到Plist文件中,这也相当于实现了一次小封装.



将这些资源准备好, 就可以进行读取了.

从plist文件中就能看出,我们代码中是用一个NSArray 数组接收plist文件中的信息的.

@property (nonatomic, strong) NSArray *appInfos;


再为setter设计功能的时候有不同的方式,也体现了不同程度的封装.

1.我们的目的是从文件中读取相应的控件描述信息给appInfo, 那么我们就直接一点,直接将文件里面读取到的内容放到appinfo中咯

NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:@"app" ofType:@"plist"];
_appInfos = [NSArray arrayWithContentsOfFile: path];


这样就OK了,但是这样在后续使用的时候, 很依赖该数组中具体封装的属性  "icon" , "name"  如果后期我在添加其他的属性,或者修改, 那么就需要到源码中不断修改.

        因为我们需要不断使用 数组和字典的语法  给控件赋值 而他们的语法明显很依赖 属性

我们应该始终秉承着这样一个原则,我们所写的主代码就是客户, 而我们图片啊,控件啊,描述信息啊就是我们商品, 任由我们的商品配方如何变化,客户要做的就是消费我们的商品

而这具体消费也是我们提供给客户对这件商品的使用方法, 比如对这件商品 吃,喝, 踹等....当我们的配方变化了的时候,这一系列消费动作还是不变的.

比如,我们给客户提供了,对文件的读取,那么我们就仅仅 一个读取接口,对于接口中配方的变化,我们不让客户知道,客户就知道读取OK了行了.

2. 我们把这几个属性进行封装,封装成一个类,这样做,我们至少以可以使用 点语法,当然这样和上面没有太大的区别,还是有很强的依赖性

也就是说我们让

@property (nonatomic, strong) NSArray *appInfos;
中的数组中存放的是对象,而不是简单几个属性.

首先创建类文件



3. 在将文件中的信息封装成类后, 仍然没有解决过度依赖的问题, 那么我们在类中定义类方法,这样我们在为数组加载数据的时候,只需要调用类方法,让类方法返回读取到的数据即可.

- (instancetype) initWithDic: (NSDictionary *) dic{
if(self = [self init]){
self.name = dic[@"name"];
self.icon = dic[@"icon"];
}
return  self;
}
+ (instancetype) appInfoWithDic: (NSDictionary *) dic{
return [[self alloc] initWithDic: dic];
}
+ (NSArray *) appINfoList{

NSBundle *bundle = [NSBundle mainBundle];

NSString *path = [bundle pathForResource:@"app" ofType:@"plist"];

NSArray *dicArray = [NSArray arrayWithContentsOfFile:path];

NSMutableArray *tmpArray = [NSMutableArray array];
// 字典转换模型
for (NSDictionary *dic in dicArray) {

CZAppinfo *appInfo = [CZAppinfo appInfoWithDic: dic];

[tmpArray addObject:appInfo];
}

return tmpArray;
}


  这样我们的setter方法实现了低耦合, 高内聚性,

- (NSArray *) appInfos{

if (_appInfos == nil) {

_appInfos = [CZAppinfo appINfoList];

}
return _appInfos;
}


=========================

好了第一部分完成了.

下一步看一下控件的方面. 对于控件方面,有两种方式,第一种每个控件单独布局,并计算坐标点.



这个控件分为三部分, 图片控件, label控件还有一个button按钮控件

那么三个控件相对与屏幕的坐标需要挨个计算这很麻烦,我们可以先生成一个基于view的子控件,然后将三个控件在添加到该子控件中,这样他们的坐标就是基于该子控件了

UIView * subView = [[UIView alloc] init];
[self.view addSubview: subView];

我们在取得了appInfos中一个对象后,就取得了整个控件的描述信息,只要再写一个方法,去分别生成三个子控件,并为该方法传递一个描述控件的对象.

由于我们对描述信息就行了封装,所以就不需要传递依赖性高的字典了.

首先写一个取数组中描述信息的对象的方法, 然后在该方法中调用子控件生成的方法

- (void) newBox{

CZAppinfo * appinfo = 取一个对象

设置先subView的坐标

传递参数

- (void) addSubView: (UIView *) subView appdesc: (CZAppinfo *) appinfo{

//不要忘记将三个子控件添加到subView上

[subView addSubview: 控件名字_View]; 
//每个控件都需要添加

}

}

这样就完成了整个界面的布局.

还有一种更简洁的方式去完成这个过程,就是利用 xib布局

创建xib文件



可以看出来, 我们把这三个控件封装到一起,  最外层是一个view控件, 这个过程也需要将三个控件添加为view子控件, 最后为view创建实现类,就像main.storyboard

实现UIView类



那xib中的view实现CZAppinfoView类(自定义控件类)



这样我们就可以在实现类中定义控件属性, 方法 并能创建基于该类的控件,  基于该类创建出来的控件就是上面我们自己自定义的控件了.

- (void) newBox{

CZAppinfo * appinfo = 取一个对象

创建自定类的控件, 设置坐标.

}

为了提高独立性,我们再在CZAppInfoView类中添加一个属性,用于描述各控件信息,这样我们可以随时使用这些信息,因为他们和控件都是类的一部分

@class CZAppinfo;

@interface CZAppInfoView : UIView

@property (weak, nonatomic) IBOutlet UIImageView *iconView;
- (IBAction)loadDown:(UIButton *)sender;

@property (weak, nonatomic) IBOutlet UILabel *nameView;

@property (nonatomic, strong) CZAppinfo * appInfo; //每个控件都会由相应的描述信息,而不依赖控件本身

+(instancetype) loadCZappinfoView;

@end

这里有一点需要注意, 我们在创建该类对象时候,其实是从该xib文件中读取对象控件的,我们需要给它实现下相应的读取方法

+(instancetype) loadCZappinfoView{
NSBundle * bundle = [NSBundle mainBundle];
//取出来的是整个组合起来的控件
CZAppInfoView * subView = [[bundle loadNibNamed: @"CZAppInfoView" owner: nil options: nil] lastObject];
<span style="white-space:pre"> </span> <span style="white-space:pre"> </span>
return subView;

}

可以看到
CZAppInfoView * subView = [[bundle loadNibNamed: @"CZAppInfoView" owner: nil options: nil] lastObject];
我们看下这个方法的定义



这行代码 和我们平时创建对象时不同,我们是使用 loadNibNamed去读取的. 而且从xib中读取的控件组合是一个数组的形式存放的(三个控件为一个元素) 
而我们需要的是整个控件,所以我们用lastObject返回这整个控件(也可以用下标, 里面就一个元素).  一定要注意这个生成过程.

还有不要忘记了,我们在该类中还有一个属性

@property (nonatomic, strong) CZAppinfo * appInfo; //每个控件都会由相应的描述信息,而不依赖控件本身
我们还需要为该类中的appInfo控件描述属性 重写setter方法, 这样当我们从- (void) newBox; 方法中取得数组中的描述信息的对象后, 可以直接为CZAppInfoView所生成的

控件, 添加描述信息.

- (void) setAppInfo:(CZAppinfo *)appInfo{
_appInfo = appInfo;
self.nameView.text = appInfo.name;
self.iconView.image = [UIImage imageNamed: appInfo.icon];

}
完整的创建控件主代码部分

- (void) newBox{
CGFloat subViewW = 100;
CGFloat subViewH = 100;
CGFloat marginX = (self.view.frame.size.width - 3 * subViewW) / 4;
CGFloat marginY = 20;
for(int i = 0; i < self.appInfos.count; i ++){
int row = i / 3;
int column = i % 3;
CGFloat subViewX = marginX + column * (marginX + subViewW);
CGFloat subViewY = 30 + row * (marginY + subViewH);

CZAppInfoView * subView = [CZAppInfoView loadCZappinfoView]; //高度封装后,创建从xib文件中的控件
[self.view addSubview: subView];
subView.frame = CGRectMake(subViewX, subViewY, subViewW, subViewH);

CZAppinfo * appinfo = self.appInfos[i];
subView.appInfo = appinfo; //通过简单的setter方法完成描述信息的录入

}
}

================================================================
做这个小程序,其实学到更多的是 封装的思想, 这远远大于程序本身.

1.在对plist文件中的字典 进行封装成对象

2.对xib文件中的控件组合 封装成对象.

3.对一系列依赖度过高的代码,独立封装成方法.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息