IOS - 自定义cell

这篇博客详述了在iOS开发中如何自定义UITableViewCell,包括数据的模拟加载、封装数据对象、使用XIB布局控件,并分别创建tableHeaderView和tableFooterView的过程。博主通过一步步操作,复习了数据加载、自定义Cell、代理协议等关键知识点。
摘要由CSDN通过智能技术生成

因为休假了好几天, 好多东西都忘记了,所以决定写一篇开发过程,找找缺漏........



模拟实现一个团购界面


底部按钮




我们知道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 的理解...................





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值