• MJRefresh分析-自定义刷新控件


    一、UITableViewCell详解

    二、自定义刷新控件步骤

    ①偏移量判断

    ②界面编写

    ③增加控件

    ④切换状态:初始-下拉刷新->header完全出现时开始刷新->数据获取完成时结束刷新(开始刷新-正在刷新-结束刷新)

    ⑤封装代码

    ⑥自动刷新和重复刷新

     代码实现:

    //
    //  ViewController.m
    //  HKUtilities
    //
    //  Created by HuJinTao on 2018/10/24.
    //  Copyright © 2018 HuJinTao. All rights reserved.
    //
    
    #import "ViewController.h"
    #import "HKMacro.h"
    @interface ViewController ()<UITableViewDelegate,UITableViewDataSource>
    
    /** 数据量 */
    @property (nonatomic, assign) NSInteger dataCount;
    /** tableView列表 */
    @property (nonatomic, strong) UITableView * tableView ;
    
    /** 上拉加载更多控件 */
    @property (nonatomic, strong) UIView *footer;
    /** 上拉加载更多控件里面的文字 */
    @property (nonatomic, strong) UILabel *footerLabel;
    /** 上拉加载更多控件时是否正在刷新 */
    @property (nonatomic, assign, getter=isFooterRefreshing) BOOL footerRefreshing ;
    
    /** 下拉刷新控件 */
    @property (nonatomic, strong) UIView *header;
    /** 下拉刷新控件里面的文字 */
    @property (nonatomic, strong) UILabel *headerLabel;
    /** 下拉加载更多控件时正在刷新 */
    @property (nonatomic, assign, getter=isHeaderRefreshing) BOOL headerRefreshing ;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.dataCount = 3;
        self.navigationItem.title = @"自定义刷新控件";
        self.view.backgroundColor = kLightGrayColor;
        self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, HK_NAVBAR_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT-HK_NAVBAR_HEIGHT) style:UITableViewStylePlain];
        [self.view addSubview:self.tableView];
        self.tableView.delegate = self;
        self.tableView.dataSource = self;
        [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
        [self setupRefresh];
        self.tableView.scrollIndicatorInsets = self.tableView.contentInset;//设置滚动条的contentInset
        self.tableView.contentInset = UIEdgeInsetsMake(0, 0, HK_TABBAR_HEIGHT, 0);
        
    }
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        //根据数据量设置footer是否显示(有数据显示,无数据隐藏)
        self.footer.hidden = (self.dataCount == 0);
        return self.dataCount;
    }
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
        cell.textLabel.text = [NSString stringWithFormat:@"自定义刷新控件-%ld",(long)indexPath.row];
        return cell;
    }
    - (void)setupRefresh {
        //FIXME:②界面编写
        //广告条
        UILabel * label = [UILabel new];
        label.backgroundColor = kBlackColor;
        label.textColor = kWhiteColor;
        label.text = @"广告";
        label.textAlignment = NSTextAlignmentCenter;
        label.frame = CGRectMake( 0, 0, 0, 30);
        self.tableView.tableHeaderView = label;
    
        //header
        UIView *header = [[UIView alloc] init];
        header.frame = CGRectMake(0, -50, self.tableView.width, 50);
        self.header = header;
        //FIXME:③增加控件
        [self.tableView addSubview:header];
    
        UILabel *headerLabel = [[UILabel alloc] init];
        headerLabel.frame = CGRectMake(0, 0, header.width, header.height);
        headerLabel.backgroundColor = kRedColor;
        headerLabel.text = @"下拉可以刷新";
        headerLabel.backgroundColor = kRedColor;
        headerLabel.textColor = kWhiteColor;
        headerLabel.textAlignment = NSTextAlignmentCenter;
        [header addSubview:headerLabel];
        self.headerLabel = headerLabel;
        //FIXME:不建议使用此属性
        //self.tableView.tableHeaderView = header;
        
        //footer
        self.footer = [[UIView alloc] init];
        self.footer.frame = CGRectMake(0, 0, self.tableView.width, 35);
        self.footerLabel = [[UILabel alloc] init];
        self.footerLabel.frame = CGRectMake(0, 0, self.footer.width, 35);
        self.footerLabel.backgroundColor = kRedColor;
        self.footerLabel.text = @"上拉可以加载更多";
        self.footerLabel.textColor = kWhiteColor;
        self.footerLabel.textAlignment = NSTextAlignmentCenter;
        [self.footer addSubview:self.footerLabel];
        self.tableView.tableFooterView = self.footer;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
        return 35;
    }
    
    #pragma mark---------代理方法------
    /** 用户松开scrollView时调用 */
    -(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
        
        //如果正在下拉刷新,直接返回
        if (self.isHeaderRefreshing) return;
        
        //当scrollView的偏移量y值 >= offsetY时,代表header已经完全出现
        CGFloat offsetY = -(self.tableView.contentInset.top + self.header.height);
        if (self.tableView.contentOffset.y <= offsetY) {
            //header开始刷新
            //FIXME:⑤封装代码
            [self headerBeginRefreshing];
        }
    }
    
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
        
        //处理Header
        [self dealHeader];
        
        //处理Footer
        [self dealFooter];
    }
    
    /** 处理Hooter */
    - (void)dealHeader {
        
        
        //如果正在下拉刷新,直接返回
        if (self.isHeaderRefreshing) return;
        //FIXME:④切换状态
        //当scrollView的偏移量y值 >= offsetY时,代表footer已经完全出现
        CGFloat offsetY = -(self.tableView.contentInset.top + self.header.height);
        if (self.tableView.contentOffset.y <= offsetY) {
            //header已经完全出现
            self.headerLabel.text = @"松开立即刷新";
            self.headerLabel.backgroundColor = kGrayColor;
        }else {
            self.headerLabel.text = @"下拉可以刷新";
            self.headerLabel.backgroundColor = kRedColor;
        }
    }
    /** 处理footer */
    - (void)dealFooter{
        //如果没有内容,则不需要执行这个操作
        if (self.tableView.contentSize.height == 0) return;
        //如果正在刷新,直接返回
        if (self.isFooterRefreshing) return;
        
        //FIXME:①偏移量判断
        //当scrollView的偏移量y值 >= offsetY时,代表footer已经完全出现
        //A.footer 完全出现时的偏移量
        //scrollView.contentOffset.y = 内容高度+底部内边距-frame高度
        //CGFloat ofsetY = self.tableView.contentSize.height + self.tableView.contentInset.bottom;
        //B.假设出现一半的时候修改状态,变为正在加载中...
        // CGFloat ofsetY = self.tableView.contentSize.height + self.tableView.contentInset.bottom - self.tableView.height-self.tableView.tableFooterView.height/2;
        //C.刚出现就修改状态
        CGFloat ofsetY = self.tableView.contentSize.height + self.tableView.contentInset.bottom - self.tableView.height-self.tableView.tableFooterView.height;
        if (self.tableView.contentOffset.y >= ofsetY && self.tableView.contentOffset.y > -(self.tableView.contentInset.top)) {
            //footer已经完全出现,并且是往下拖拽
            //进入刷新状态
            //FIXME:⑤封装代码
            [self footerBeginRefreshing];
        }
    }
    
    #pragma mark - header
    - (void)headerBeginRefreshing  {
        
        //如果正在刷新,直接返回
        if (self.isHeaderRefreshing) return;
        
        //header已经完全出现
        self.headerLabel.text = @"正在刷新数据...";
        self.headerLabel.backgroundColor = kBlueColor;
        self.headerRefreshing = YES;
        //增加内边距
        [UIView animateWithDuration:0.25 animations:^{
            UIEdgeInsets inset = self.tableView.contentInset;
            inset.top += self.header.height;
            self.tableView.contentInset = inset;
            //FIXME:⑥自动刷新和重复刷新
            //修改偏移量(稳住刷新状态)
            self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, - inset.top);
        }];
        
        HKLog(@"发送请求给服务器。下拉刷新数据");
        //发送请求给服务器。下拉刷新数据
        [self loadNewData];
    }
    - (void)headerEndRefreshing {
        self.headerRefreshing = NO;
        //减小内边距
        [UIView animateWithDuration:0.25 animations:^{
            UIEdgeInsets inset = self.tableView.contentInset;
            inset.top -= self.header.height;
            self.tableView.contentInset = inset;
        }];
        //self.headerLabel.text = @"下拉可以刷新";
        //self.headerLabel.backgroundColor = kRedColor;
    }
    
    #pragma mark - footer
    
    - (void)footerBeginRefreshing {
        
        //如果正在刷新,直接返回
        if (self.isFooterRefreshing) return;//保证同一时间只执行一次
        
        //进入刷新状态
        self.footerRefreshing = YES;
        self.footerLabel.text = @"正在加载更多数据...";
        self.footerLabel.backgroundColor = kBlueColor;
        
        //发送请求给服务器-加载更多数据
        HKLog(@"发送请求给服务器-加载更多数据");
        [self loadMoreData];
    }
    
    - (void)footerEndRefreshing
    {
        self.footerRefreshing = NO;
        self.footerLabel.text = @"上拉可以加载更多";
        self.footerLabel.backgroundColor = kRedColor;
    }
    
    #pragma mark - 数据处理
    /** 发送请求给服务器。下拉刷新数据 */
    - (void)loadNewData {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            //服务器的数据回来了
            self.dataCount = 3;
            [self.tableView reloadData];
            //结束刷新
            [self headerEndRefreshing];
        });
    }
    /** 发送请求给服务器-加载更多数据 */
    - (void)loadMoreData {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            //服务器请求回来
            self.dataCount +=5;
            [self.tableView reloadData];
            //结束刷新
            [self footerEndRefreshing];
        });
    }
    
    @end


    MethodList

    效果图

     

    Demo地址

    三、第三方MJRefresh类结构图

    MJRefresh 类结构图:

    MIRefreshComponent:刷新控件的基类

    MJRefreshHeader:基础的下拉刷新控件(Header)

    MJRefreshStateHeader:带有状态文字的下拉刷新控件

    MJRefreshNormalHeader:默认的下拉刷新控件

    MJRefreshGitHeader:带动图的下拉刷新控件

     

    MJRefreshFooter:基础的下拉刷新控件(Footer)

    MJRefreshBackFooter:会回弹到底部的下拉刷新控件

    MJRefreshAutoFooter:会自动刷新的上拉刷新控件

    MJRefreshBackStateFooter:带有状态文字的上拉加载控件MJRefreshAutoStateFooter:带有状态文字的上拉加载控件

    MIRefreshBackNormalFooter:默认的上拉加载控件

    MIRefreshBackGifFooter:带动图的上拉加载控件

    MIRefreshAutoNormalFooter:默认的上拉加载控件

    MIRefreshAutoGifFooter:带动图的上拉加载控件

    使用:

    + (MJRefreshGifHeader *)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock{

        

        MJRefreshGifHeader *header = [MJRefreshGifHeader headerWithRefreshingBlock:refreshingBlock];

        header.lastUpdatedTimeLabel.hidden = YES;

        header.stateLabel.hidden = YES;

        

        NSMutableArray *a = @[].mutableCopy;

        for (int i =0 ; i<4; i++) {

            [a addObject:[UIImage imageNamed:[NSString stringWithFormat:@"下啦刷新-%d",i+1]]];

        }

        [header setImages:a duration:0.5f forState:MJRefreshStateRefreshing];

        [header setImages:@[[UIImage imageNamed:@"松手加载-1"],[UIImage imageNamed:@"松手加载-2"]] duration:0.3f forState:MJRefreshStatePulling];

        [header setImages:@[[UIImage imageNamed:@"用力一点-1"],[UIImage imageNamed:@"用力一点-2"]] duration:0.5f forState:MJRefreshStateIdle];

        header.backgroundColor = [UIColor clearColor];

        return header;

    }

     

    + (MJRefreshAutoNormalFooter *)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock{

        MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:refreshingBlock];

        footer.backgroundColor = [UIColor clearColor];

        footer.automaticallyRefresh = YES;

        footer.triggerAutomaticallyRefreshPercent = -3.0;

        return footer;

    }

    _collectionView = [[XBHomeCollectionView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];

        _collectionView.homeDelegate = self;

        _collectionView.backgroundColor = kBlackColor;

        XBWeakSelf

        _collectionView.mj_header = [XBTool headerWithRefreshingBlock:^{

            XBStrongSelf

            [self reloadData:NO];

        }];

        _collectionView.mj_footer = [XBTool footerWithRefreshingBlock:^{

            XBStrongSelf

            [self reloadData:YES];

        }];

        [self.view addSubview:_collectionView];

        self.collectionView.mj_footer.hidden = YES;

     

    -(void)reloadData:(BOOL)append {

        if (!append) {

            self.currentPage = 1;

            [_dataArray removeAllObjects];

        }else {

            self.currentPage += 1;

        }

        [self getData:append];

    }

    - (void)getData:(BOOL)append{

        XBWeakSelf

        //获取精选列表

        NSDictionary * dic = @{@"currentPage":[NSString stringWithFormat:@"%ld",(long)self.currentPage],

                               @"pageSize":[NSString stringWithFormat:@"%ld",(long)DefaultPageSize]

                               };

        [[HKNetService jsonManager] sendPOSTWithUrl:SERVER_HOMEHANDPICKLIST params:dic success:^(HKURLResponse *response, HKResultMessage *resultMessage) {

            XBStrongSelf

            NSDictionary * dic = response.result;

            NSArray * array = [dic valueForKey:@"data"];

     

            for (NSDictionary *dic in array) {

                XBHomeModel * model = [[XBHomeModel alloc] init];

                [model setValuesForKeysWithDictionary:dic];

                [self.dataArray addObject:model];

            }

            if ([response.responseData isKindOfClass:[NSDictionary class]] || [response.responseData isKindOfClass:[NSArray class]]) {

                NSString *b = [response.responseData JF_jsonString];

                [XBUserDefaults setObject:b forKey:HandpickListData];

                [XBUserDefaults synchronize];

            }

            [self endRefreshing];

            if (self.dataArray.count) {

                dispatch_async(dispatch_get_main_queue(), ^{

                    [self.collectionView setDataArray:self.dataArray];

                });

            }

            if (array.count<DefaultPageSize) {

                self.collectionView.mj_footer.hidden = YES;

            }

            if (array.count == 0) {

                [self.collectionView.mj_footer endRefreshingWithNoMoreData];

            }

     

        } failure:^(HKURLResponse *response, HKResultMessage *resultMessage) {

            XBStrongSelf

            [self endRefreshing];

            [HKToast showErrorMessage:resultMessage];

        }];

    }

    - (void)endRefreshing {

        [self.collectionView.mj_header endRefreshing];

        [self.collectionView.mj_footer endRefreshing];

    }

    下拉刷新-动画图片

    // 设置回调(一旦进入刷新状态,就调用target的action,也就是调用self的loadNewData方法)
    MJRefreshGifHeader *header = [MJRefreshGifHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)];
    // 设置普通状态的动画图片
    [header setImages:idleImages forState:MJRefreshStateIdle];
    // 设置即将刷新状态的动画图片(一松开就会刷新的状态)
    [header setImages:pullingImages forState:MJRefreshStatePulling];
    // 设置正在刷新状态的动画图片
    [header setImages:refreshingImages forState:MJRefreshStateRefreshing];
    // 设置header
    self.tableView.mj_header = header;

  • 相关阅读:
    JAVAWEB进行PC支付宝支付
    SpringBoot 设置请求字符串格式为UTF-8
    SpringBoot 处理跨域请求问题
    Python 包制作
    F5 开发
    CentOS7 部署nfs服务
    Debian 9 部分快捷键失效问题
    Win10提示 该文件没有与之关联的程序来执行操作
    Debian 9 编译Python
    debian 9 安装远程桌面
  • 原文地址:https://www.cnblogs.com/StevenHuSir/p/MJRefreshCustom.html
Copyright © 2020-2023  润新知