IOS开发学习笔记十五 为UITableView控件添加Header和Footer

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/abc6368765/article/details/82697571

效果图:项目地址

这里写图片描述

  • 添加图片素材,添加plist文件,添加名为CZGoods的module文件
@implementation CZGoods

- (instancetype)initWithDict:(NSDictionary *)dict
{
    if (self = [super init]) {
        [self setValuesForKeysWithDictionary:dict];
    }
    return self;
}

+ (instancetype)goodsWithDict:(NSDictionary *)dict
{
    return [[self alloc] initWithDict:dict];
}
@end
  • 在ViewController里面添加UITableView控件,把plist文件转化成module数据,然后把数据源设置给UiTableView

ViewController代码:

#import "ViewController.h"
#import "CZGoods.h"
#import "CZGoodsCell.h"
#import "CZFooterView.h"
#import "CZHeaderView.h"

@interface ViewController () <UITableViewDataSource, CZFooterViewDelegate>

// 用来存储所有的团购商品的数据
@property (nonatomic, strong) NSMutableArray *goods;

@property (weak, nonatomic) IBOutlet UITableView *tableView;
@end

@implementation ViewController


#pragma mark - 懒加载数据
- (NSMutableArray *)goods
{
    if (_goods == nil) {
        NSString *path = [[NSBundle mainBundle] pathForResource:@"tgs.plist" ofType:nil];
        NSArray *arrayDict = [NSArray arrayWithContentsOfFile:path];
        NSMutableArray *arrayModels = [NSMutableArray array];
        for (NSDictionary *dict in arrayDict) {
            CZGoods *model = [CZGoods goodsWithDict:dict];
            [arrayModels addObject:model];
        }
        _goods = arrayModels;
    }
    return _goods;
}

#pragma mark - 数据源方法
//- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
//{
//    return 1;
//}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.goods.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 1. 获取模型数据
    CZGoods *model = self.goods[indexPath.row];

    // 2. 创建单元格
    // 通过xib的方式来创建单元格
    CZGoodsCell *cell = [CZGoodsCell goodsCellWithTableView:tableView];


    // 3. 把模型数据设置给单元格
    // 在控制器中直接为cell的每个子控件赋值数据造成的问题:
    // 1. 控制器强依赖于Cell, 一旦cell内部的子控件发生了变化, 那么控制器中的代码也得改(这就造成了紧耦合)
    // 2. cell的封装不够完整, 凡是用到这个cell的地方, 每次都要编写为cell的子控件依次赋值的语句,比如:cell.xxx = model.title;
    // 3. 解决: 直接把模型传递给自定义Cell, 然后在自定义cell内部解析model中的数据赋值给自定义cell内部的子控件。
    cell.goods = model;

    // 4.返回单元格
    return cell;
}



#pragma mark - 隐藏状态栏
- (BOOL)prefersStatusBarHidden
{
    return YES;
}





- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.rowHeight = 44;

    // 设置UITableView的footerView
//    UIButton *btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
//    
//    btn.backgroundColor = [UIColor redColor];
//    btn.frame = CGRectMake(20, 50, 30, 100);
//    // tableView的footerView的特点: 只能修改x和height的值, Y 和 width不能改。
//    self.tableView.tableFooterView = btn;



    // 通过Xib设置UITableView的footerView
    CZFooterView *footerView = [CZFooterView footerView];
    // 设置footerView的代理
    footerView.delegate = self;
    self.tableView.tableFooterView = footerView;


    // 创建Header View
    CZHeaderView *headerView = [CZHeaderView headerView];
    self.tableView.tableHeaderView = headerView;





}

#pragma mark - CZFooterView的代理方法

- (void)footerViewUpdateData:(CZFooterView *)footerView
{
    // 3. 增加一条数据


    // 3.1 创建一个模型对象
    CZGoods *model = [[CZGoods alloc] init];
    model.title = @"驴肉火烧";
    model.price = @"6.0";
    model.buyCount = @"1000";
    model.icon = @"37e4761e6ecf56a2d78685df7157f097";

    // 3.2 把模型对象加到控制器的goods集合当中
    [self.goods addObject:model];

    // 4. 刷新UITableView
    [self.tableView reloadData];

//    // 局部刷新(只适用于UITableView总行数没有发生变化的情况)
//    NSIndexPath *idxPath = [NSIndexPath indexPathForRow:self.goods.count - 1 inSection:0];
//    [self.tableView reloadRowsAtIndexPaths:@[idxPath] withRowAnimation:UITableViewRowAnimationLeft];


    // 5. 把UITableView中的最后一行的数据滚动到最上面
     NSIndexPath *idxPath = [NSIndexPath indexPathForRow:self.goods.count - 1 inSection:0];
    [self.tableView scrollToRowAtIndexPath:idxPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

里面用到了自定义的UITableCell,UITableHeader,UITableFooter的.xib文件

  • CZGoodsCell代码:

#import "CZGoodsCell.h"
#import "CZGoods.h"

@interface CZGoodsCell ()
@property (weak, nonatomic) IBOutlet UIImageView *imgViewIcon;
@property (weak, nonatomic) IBOutlet UILabel *lblTitle;
@property (weak, nonatomic) IBOutlet UILabel *lblPrice;
@property (weak, nonatomic) IBOutlet UILabel *lblBuyCount;

@end


@implementation CZGoodsCell

+ (instancetype)goodsCellWithTableView:(UITableView *)tableView
{
    static NSString *ID = @"goods_cell";
    CZGoodsCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[[NSBundle mainBundle] loadNibNamed:@"CZGoodsCell" owner:nil options:nil] firstObject];
    }
    return cell;
}


- (void)setGoods:(CZGoods *)goods
{
    _goods = goods;
    // 把模型的数据设置给子控件
    self.imgViewIcon.image = [UIImage imageNamed:goods.icon];
    self.lblTitle.text = goods.title;
    self.lblPrice.text = [NSString stringWithFormat:@"¥ %@", goods.price];
    self.lblBuyCount.text = [NSString stringWithFormat:@"%@ 人已购买", goods.buyCount];
}

- (void)awakeFromNib {
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

@end
  • CZFooterView代码:

footer

#import "CZFooterView.h"

@interface CZFooterView ()
@property (weak, nonatomic) IBOutlet UIButton *btnLoadMore;
@property (weak, nonatomic) IBOutlet UIView *waitingView;
- (IBAction)btnLoadMoreClick;
@end


@implementation CZFooterView

+ (instancetype)footerView
{
    CZFooterView *footerView = [[[NSBundle mainBundle] loadNibNamed:@"CZFooterView" owner:nil options:nil] lastObject];
    return footerView;
}


/**
*  加载更多按钮的单击事件
*/
- (IBAction)btnLoadMoreClick {
    // 1. 隐藏"加载更多"按钮
    self.btnLoadMore.hidden = YES;

    // 2. 显示"等待指示器"所在的那个UIView
    self.waitingView.hidden = NO;

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        // 3. 调用代理方法实现下面的功能
        // 调用footerViewUpdateData方法之前, 为了保证调用不出错, 所以要先判断一下代理对象是否真的实现了这个方法, 如果实现了这个方法再调用, 否则不调用.
        if ([self.delegate respondsToSelector:@selector(footerViewUpdateData:)]) {
            // 3. 增加一条数据
            // 3.1 创建一个模型对象
            // 3.2 把模型对象加到控制器的goods集合当中
            // 4. 刷新UITableView
            [self.delegate footerViewUpdateData:self];
        }


        // 4. 显示"加载更多"按钮
        self.btnLoadMore.hidden = NO;

        // 5. 隐藏"等待指示器"所在的那个UIView
        self.waitingView.hidden = YES; 
    });

}
@end
  • CZHeaderView代码:

header

#import "CZHeaderView.h"


@interface CZHeaderView ()<UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollViewSlider;
@property (weak, nonatomic) IBOutlet UIPageControl *pageControl;

// 创建一个用来引用计时器对象的属性
@property (nonatomic, strong) NSTimer *timer;

@end

@implementation CZHeaderView

// 实现UIScrollView的滚动方法
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{

    // 如何计算当前滚动到了第几页?
    // 1. 获取滚动的x方向的偏移值
    CGFloat offsetX = scrollView.contentOffset.x;
    // 用已经偏移了得值, 加上半页的宽度
    offsetX = offsetX + (scrollView.frame.size.width * 0.5);

    // 2. 用x方向的偏移的值除以一张图片的宽度(每一页的宽度),取商就是当前滚动到了第几页(索引)
    int page = offsetX / scrollView.frame.size.width;

    // 3. 将页码设置给UIPageControl
    self.pageControl.currentPage = page;

    NSLog(@"滚了,要在这里根据当前的滚动来计算当前是第几页。");
}

// 实现即将开始拖拽的方法
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    // 停止计时器
    // 调用invalidate一旦停止计时器, 那么这个计时器就不可再重用了。下次必须重新创建一个新的计时器对象。
    [self.timer invalidate];

    // 因为当调用完毕invalidate方法以后, 这个计时器对象就已经废了,无法重用了。所以可以直接将self.timer设置为nil
    self.timer = nil;
}

// 实现拖拽完毕的方法
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{

    // 重新启动一个计时器
    self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(scrollImage) userInfo:nil repeats:YES];

    // 再次修改一下新创建的timer的优先级
    // 修改self.timer的优先级与控件一样
    // 获取当前的消息循环对象
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    // 改变self.timer对象的优先级
    [runLoop addTimer:self.timer forMode:NSRunLoopCommonModes];
}

// 当这个方法被执行的时候就表示CZHeaderView已经从xib中创建好了,那么也就意味着在CZHeaderView
// 中的所有的子控件也都创建好了。所以就可以使用UIScrollView了。
- (void)awakeFromNib
{

    // 在这里就表示CZHeaderView已经从xib中创建好了。
    //self.scrollView.contentSize = ...;
    // 动态创建UIImageView添加到UIScrollView中

    CGFloat imgW = 375;
    CGFloat imgH = 168;
    CGFloat imgY = 0;

    // 1. 循环创建5个UIImageView添加到ScrollView中
    for (int i = 0; i < 5; i++) {
        // 创建一个UIImageView
        UIImageView *imgView = [[UIImageView alloc] init];

        // 设置UIImageView中的图片
        NSString *imgName = [NSString stringWithFormat:@"img_%02d", i + 1];
        imgView.image = [UIImage imageNamed:imgName];

        // 计算每个UIImageView在UIScrollView中的x坐标值
        CGFloat imgX = i * imgW;
        // 设置imgView的frame
        imgView.frame = CGRectMake(imgX, imgY, imgW, imgH);

        // 把imgView添加到UIScrollView中
        [self.scrollViewSlider addSubview:imgView];
    }


    // 设置UIScrollView的contentSize(内容的实际大小)
    CGFloat maxW = self.scrollViewSlider.frame.size.width * 5;
    self.scrollViewSlider.contentSize = CGSizeMake(maxW, 0);


    // 实现UIScrollView的分页效果
    // 当设置允许分页以后, UIScrollView会按照自身的宽度作为一页来进行分页。
    self.scrollViewSlider.pagingEnabled = YES;

    // 隐藏水平滚动指示器
    self.scrollViewSlider.showsHorizontalScrollIndicator = NO;

    // 指定UIPageControl的总页数
    self.pageControl.numberOfPages = 5;

    // 指定默认是第0页
    self.pageControl.currentPage = 0;


    // 创建一个"计时器"控件NSTimer控件
    // 通过scheduledTimerWithInterval这个方法创建的计时器控件, 创建好以后自动启动
    self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(scrollImage) userInfo:nil repeats:YES];

    // 修改self.timer的优先级与控件一样
    // 获取当前的消息循环对象
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    // 改变self.timer对象的优先级
    [runLoop addTimer:self.timer forMode:NSRunLoopCommonModes];

}

// 自动滚动图片的方法
// 因为在创建计时器的时候, 指定了时间间隔是1.0秒,所以下面这个方法每隔一秒钟执行一次
- (void)scrollImage
{
    // 每次执行这个方法的时候, 都要让图片滚动到下一页
    // 如何让UIScrollView滚动到下一页?
    // 1. 获取当前的UIPageControl的页码
    NSInteger page = self.pageControl.currentPage;
    NSLog(@"当前page ==== %d",page);

    // 2. 判断页码是否到了最后一页, 如果到了最后一页, 那么设置页码为0(回到第一页)。如果没有到达最后一页, 则让页码+1
    if (page == self.pageControl.numberOfPages - 1) {
        // 表示已经到达最后一页了
        page = 0; // 回到第一页
    } else {
        page++;
    }
    NSLog(@"计算后的page ==== %d",page);
    // 3. 用每页的宽度 * (页码 + 1) == 计算除了下一页的contentOffset.x
    CGFloat offsetX =  self.scrollViewSlider.frame.size.width*page;
    NSLog(@"计算后的offsetX ==== %d",offsetX);

    // 4. 设置UIScrollView的contentOffset等于新的偏移的值
    [self.scrollViewSlider setContentOffset:CGPointMake(offsetX, 0) animated:YES];


    // 如果图片现在已经滚动到最后一页了, 那么就滚动到第一页

}


+ (instancetype)headerView
{
    CZHeaderView *headerView = [[[NSBundle mainBundle] loadNibNamed:@"CZHeaderView" owner:nil options:nil] firstObject];
    return headerView;
}


@end

猜你喜欢

转载自blog.csdn.net/abc6368765/article/details/82697571
今日推荐