因为休假了好几天, 好多东西都忘记了,所以决定写一篇开发过程,找找缺漏........
模拟实现一个团购界面
底部按钮
我们知道tableview 是可以用三部分描述的
tableHeaderView + tableFooterView + cell
我们先实现cell部分, 在此之前先把数据模拟加载.这次详细的复习下数据的模拟加载.
1.数据加载
将数据封装成对象, 然后放到数组中.
我们会在内存中用一个数组来存放数据, 我们希望将数据封装起来, 所以创建一个类, 用来描述所有的数据.
(根据plist文件在类中定义属性, 属性名字最好和plist中的属性名一样,这样我们可以使用KVC的方式读取. 对于源数据封装到plist中, 我们最常见的是每一条记录封装成一个字典项,这样当我们读取的时候可以用KVC的 setValuesForKeysWithDictionary: (NSDictionary *) ; 语句进行读取.)
在类中定义完属性, 还需要定义方法, 用来初始化并从文件中读取数据
- (instancetype)initWithDic:(NSDictionary *)dic;
+ (instancetype)groupBuyingWithDic:(NSDictionary *)dic; //用类方法, 直接返回用字典转换为模型的对象.
+ (NSMutableArray *)groupBuyingsList;
实现:
- (instancetype)initWithDic:(NSDictionary *)dic
{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dic]; //读取数据
}
return self;
}
+ (instancetype)groupBuyingWithDic:(NSDictionary *)dic
{
return [[self alloc] initWithDic:dic];
}
+ (NSMutableArray *)groupBuyingsList //将封装好的对象 加载到数组中, 返回给内存中模拟的数组
{
//加载plist
NSString *path = [[NSBundle mainBundle] pathForResource:@"tgs" ofType:@"plist"];
NSArray *dicArray = [NSArray arrayWithContentsOfFile:path];
//字典转模型
NSMutableArray *tmpArray = [NSMutableArray array]; //将要返回给内存中的数组
for (NSDictionary *dic in dicArray) {
CZGroupBuying *groupBuying = [CZGroupBuying groupBuyingWithDic:dic];
[tmpArray addObject:groupBuying];
}
return tmpArray;
}
当我们的字典项中还嵌套了数组, 数组又嵌套了字典的时候 ,我们需要两次转换..
比如这种..
+ (NSArray *) carGroupsList{
NSString * path = [[NSBundle mainBundle] pathForResource: @"cars_total" ofType: @"plist"];
NSArray * dicArray = [NSArray arrayWithContentsOfFile: path];
NSMutableArray * tmpArray = [NSMutableArray array];
for (NSDictionary * dic in dicArray) {
AMCarGroup * cargroup = [AMCarGroup carGroupWithDic: dic];
NSMutableArray * tmpCarArray = [NSMutableArray array];
for (NSDictionary * tmpdic in cargroup.cars) { //在加一次循环,读取嵌套的字典中的内容...
AMcar * car = [AMcar carWithDic: tmpdic];
[tmpCarArray addObject: car];
}
cargroup.cars = tmpCarArray;
[tmpArray addObject: cargroup];
}
return tmpArray;
}
由于上面的封装, 所以在给内存中模拟的数组, 加载数据就很简单了...
- (NSMutableArray *)groupBuyings
{
if (_groupBuyings ==nil) {
_groupBuyings = [CZGroupBuying groupBuyingsList];
}
return _groupBuyings;
}
2. 自定义cell
大多时候,我们需要进行自定义cell, 所以要用到xib, 在xib中布局好控件后,我们将它封装成类. 并且 这个类是继承 UITableViewCell类的, 这一点很重要.
但是,控件还没有描述信息, 所以我们再在类中添加一个信息的属性.
@class CZGroupBuying;
@interface CZGroupBuyingCell : UITableViewCell
@property (nonatomic, strong) CZGroupBuying *groupBuying;
+ (instancetype)groupBuyingCellWithTableView:(UITableView *)tableView;
@end
因为我们已经将描述信息封装成了类,所以我们字需要定义一个对象属性即可.
而我们定义的类方法,是直接从xib中读取控件, 这里需要主要的是, 需要传入一个UITableView * 参数, 因为在创建缓冲池的时候需要用到.
实现:
+ (instancetype)groupBuyingCellWithTableView:(UITableView *)tableView
{
static NSString *reuseId = @"gb";
CZGroupBuyingCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:@"CZGroupBuyingCell" owner:nil options:nil] lastObject];
}
return cell;
}
//重写属性的setter方法,给子控件赋值
- (void)setGroupBuying:(CZGroupBuying *)groupBuying
{
_groupBuying = groupBuying;
self.titleView.text = groupBuying.title;
self.priceView.text = [NSString stringWithFormat:@"¥ %@",groupBuying.price];
self.buyCountView.text = [NSString stringWithFormat:@"%@人已购买",groupBuying.buyCount];
self.iconView.image = [UIImage imageNamed:groupBuying.icon];
}
这里再说明下, 我们将xib中的控件, 封装成的类是继承UITableViewCell类的..然后通过类方法 ,返回xib中读取到的对象(控件).
最后在数据源协议方法中调用
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//1 创建可重用的自定义的cell
CZGroupBuyingCell *cell = [CZGroupBuyingCell groupBuyingCellWithTableView:tableView];
//2 设置cell内部的子控件
CZGroupBuying *gb = self.groupBuyings[indexPath.row];
cell.groupBuying = gb; //重写的setter方法
//3 返回
return cell;
}
3. 创建tableHeaderView
因为tableHeaderView是一个UIView所以可以很随意的使用控件..这里我们用到的是一个scrollView, 正好复习下,所详细说一下...
由于这个头部控件 ,是多个控件所以用xib 组合一下, 最重要的还是scrollView.
当我们在xib中创建好scrollView的时候 ,由于我们需要大量操作他,我们使用最简单的方法, 直接连线解决, 这样就可以得到一个outlet属性...
首先定义xib的实现类, 用类方法返回加载到的控件.因为加载到的控件里面的scrollView需要滚动, 他的滚动时机,需要一个 方法控制
- (void)awakeFromNib
{
CGFloat iconW = self.scrollView.frame.size.width;
CGFloat iconH = self.scrollView.frame.size.height;
for (int i = 0; i < 5; i++) {
NSString *imgName = [NSString stringWithFormat:@"ad_%02d",i];
UIImageView *iconView = [[UIImageView alloc] init];
[self.scrollView addSubview:iconView];
iconView.image = [UIImage imageNamed:imgName];
CGFloat iconX = i * iconW;
CGFloat iconY = 0;
iconView.frame = CGRectMake(iconX, iconY, iconW, iconH);
}
self.scrollView.contentSize = CGSizeMake(5 * iconW, 0);
}
这个函数 是在xib加载完成的时候 触发的..我们只需要实现他即可....
注意这次是代码实现的imageview控件, 需要我们设置imageview的位置
iconView.frame = CGRectMake(iconX, iconY, iconW, iconH);
最后还需要设置 scrollview的滚动范围.
self.scrollView.contentSize = CGSizeMake(5 * iconW, 0);
4. 创建tableFooterView
同样还是xib先实现
这个布局很具有代表性, 首先最外层是一个View, 所有的控件都在这个View中, 然后根据效果, 再点击了加载更多按钮后要显示一个lable..
这样当需要要操作某些控件的时候, 都能清晰的找到其父控件 , 我们只需要对其父控件进行隐藏 或显示 , 就可以不必挨个的对其子控件 设置隐藏或显示...
这想说的是, 我们使用了一个代理 去实现点击按钮后要触发的事件...复习下代理:
1.给相应的类定义代理协议, 并声明 该类还具备的某些能力
@protocol CZFooterViewDelegate <NSObject> //定义协议
@optional
- (void)footerViewDidClickedLoadMoreBtn:(CZFooterView *)footerView; //具备的能力
@end
上面的footerViewDidClickedLoadMoreBtn:(CZFooterView *)footerView 参数是一个 czfooterview * 类型的. 这个footerview 参数就是从xib中读取的控件..
2. 在类添加 与 代理 沟通的媒介 (代理属性)
@interface CZFooterView : UIView
//2 定义代理属性
@property (nonatomic, weak) id<CZFooterViewDelegate> delegate; //代理属性
+ (instancetype)footerView;
@end
3. 用点击按钮所注册的事件处理方法中, 调用代理属性 去调用协议中的方法
- (IBAction)loadMoreClick {
self.loadMoreBtn.hidden = YES;
self.loadMoreView.hidden = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.loadMoreBtn.hidden = NO;
self.loadMoreView.hidden = YES;
//3 向代理对象发送消息
if ([self.delegate respondsToSelector:@selector(footerViewDidClickedLoadMoreBtn:)]) {
[self.delegate footerViewDidClickedLoadMoreBtn:self];
}
});
}
上面代码中的self 就是xib中所读取到的控件, 因为我们的xib文件的实现类就是CZfooterView, 虽然这个类中没有其他属性, 看似是一个空类, 但是我们仍可以用self 来描述他所产生的对象.
self --------> CZfooterVIew对象.
我们在写方法的时候, 我们是在为对象 写方法...类方法 也是 内部调用了对象的.
再加深下对self 的理解...................