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;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM