下拉刷新和上拉加载之MJRefresh篇(一)

版本历史

版本号 修改时间
V1.0 2017.05.01

前言

在我们的app中加载数据,很多时候都需要进行上拉加载和下拉刷新,特别是像今日头条这样的门户网站,每个页面给用户提供20条左右的数据,根据用户的喜欢选择是否刷新,才请求服务器给出分页的数据。这里我们可以自定义上拉加载和下拉刷新,但是也可以采用第三方框架,我们这篇主要就是说的是应用较为广泛的第三方框架MJRefresh,此篇为基础入门篇,专门为那些刚入门同学的进行引导,下面主要的内容就是对MJRefresh进行概览并给出一个最简单的例子。下面就开始吧。开始之前先给出MJRefreh的github地址。
MJRefrsh-github地址

MJRefresh概览

适用范围

MJRefresh适用哪些控件的刷新呢?github官网上给出了四个,其实不难猜到就是主要的那几个可以滚动的控件(UITextView除外),主要就是四个:UIScrollViewUITableViewUICollectionViewUIWebView

API集成

主要的集成方式有两种:

  1. 自动集成:通过cocoapods集成。
  2. 手动集成:通过github下载,手动拖动到项目中。

API结构

我们先看一下有哪些源文件。

API结构-1
API结构-2

这个家族的文件就目前的更新情况而言,已经全部在这里了。我们在看一下下面这个图表。

API图表

下面对这个图标进行说明:

  • 红色的部分,可直接使用
  • 下拉刷新 1)Normal: MJRefreshNormalHeader 2) Gif:MJRefreshGifHeader
  • 上拉刷新:1)自动刷新 Normal:MJRefreshAutoNormalFooter 和 Gif:MJRefreshAutoGifFooter 2)自动回弹 Normal:MJRefreshBackNormalFooter 和 Gif:MJRefreshBackGifFooter。
  • 其他颜色的部分不可以直接使用。

除了我们直接用,我们还可以DIY刷新,使用下面的。

DIY刷新

API中几个重要的类

1. MJRefreshComponent.h
/** The Base Class of refresh control */

@interface MJRefreshComponent : UIView

#pragma mark -  Control the state of Refresh 

/** BeginRefreshing */
- (void)beginRefreshing;

/** EndRefreshing */
- (void)endRefreshing; 

/** IsRefreshing */
- (BOOL)isRefreshing;

#pragma mark - Other

* According to the drag ratio to change alpha automatically 

@property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha;

@end

2. MJRefreshHeader.h

@interface MJRefreshHeader : MJRefreshComponent

/** Creat header */
+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock;

/** Creat header */
+ (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;

/** This key is used to storage the time that the last time of drown-down successfully */
@property (copy, nonatomic) NSString *lastUpdatedTimeKey;

/** The last time of drown-down successfully */
@property (strong, nonatomic, readonly) NSDate *lastUpdatedTime;

/** Ignored scrollView contentInset top */
@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop;

@end

3. MJRefreshFooter.h

 @interface MJRefreshFooter : MJRefreshComponent

/** Creat footer */
+ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock;

/** Creat footer */
+ (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;

/** NoticeNoMoreData */
- (void)noticeNoMoreData;

/** ResetNoMoreData(Clear the status of NoMoreData ) */
- (void)resetNoMoreData;

/** Ignored scrollView contentInset bottom */
@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetBottom;

/** Automaticlly show or hidden by the count of data(Show-have data,Hidden- no data) */
@property (assign, nonatomic) BOOL automaticallyHidden;@end


4. MJRefreshAutoFooter.h

@interface MJRefreshAutoFooter : MJRefreshFooter

/** Is Automatically Refresh(Default is Yes) */
@property (assign, nonatomic, getter=isAutomaticallyRefresh) BOOL automaticallyRefresh;

/** When there is much at the bottom of the control is automatically refresh(Default is 1.0,Is at the bottom of the control appears in full, will refresh automatically) */
@property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent;

@end

几种使用方式

下面简单的介绍几种使用方式,内容来源于github。谢谢每一个作者的分享,谢谢大家。

下拉刷新

1. Default模式刷新

self.tableView.header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
   //Call this Block When enter the refresh status automatically 
}];

或

// Set the callback(Once you enter the refresh status,then call the action of target,that is call [self loadNewData])
self.tableView.header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)];

// Enter the refresh status immediately
[self.tableView.header beginRefreshing];

看下面的gif图。

default模式

2. 带有动画图片的刷新

// Set the callback(一Once you enter the refresh status,then call the action of target,that is call [self loadNewData])
MJRefreshGifHeader *header = [MJRefreshGifHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)];

// Set the ordinary state of animated images
[header setImages:idleImages forState:MJRefreshStateIdle];

// Set the pulling state of animated images(Enter the status of refreshing as soon as loosen)
[header setImages:pullingImages forState:MJRefreshStatePulling];

// Set the refreshing state of animated images
[header setImages:refreshingImages forState:MJRefreshStateRefreshing];

// Set header
self.tableView.mj_header = header;

我们看一下下面的gif图。

animate-image

3. 隐藏时间的刷新

// Hide the time
header.lastUpdatedTimeLabel.hidden = YES;

我们看下面的gif图。

hide-time-fresh

4. 隐藏状态和时间的刷新

// Hide the time
header.lastUpdatedTimeLabel.hidden = YES;

// Hide the status
header.stateLabel.hidden = YES;


看下下面的gif图。

hide_time_status_fresh

5. DIY文案刷新

// Set title
[header setTitle:@"Pull down to refresh" forState:MJRefreshStateIdle];
[header setTitle:@"Release to refresh" forState:MJRefreshStatePulling];
[header setTitle:@"Loading ..." forState:MJRefreshStateRefreshing];

// Set font
header.stateLabel.font = [UIFont systemFontOfSize:15];
header.lastUpdatedTimeLabel.font = [UIFont systemFontOfSize:14];

// Set textColor
header.stateLabel.textColor = [UIColor redColor];
header.lastUpdatedTimeLabel.textColor = [UIColor blueColor];

看一下gif图。

DIY_fresh

6. 自定义控件的刷新

self.tableView.mj_header = [MJDIYHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)];
// Implementation reference to MJDIYHeader.h和MJDIYHeader.m

看下面的gif图。

control_fresh

上拉刷新

1. Default模式刷新

self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
    //Call this Block When enter the refresh status automatically
}];
或
// Set the callback(Once you enter the refresh status,then call the action of target,that is call [self loadMoreData])
self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)];

看下面的gif图。

default_fresh

2. 动画刷新

// Set the callback(Once you enter the refresh status,then call the action of target,that is call [self loadMoreData])
MJRefreshAutoGifFooter *footer = [MJRefreshAutoGifFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)];

// Set the refresh image
[footer setImages:refreshingImages forState:MJRefreshStateRefreshing];

// Set footer
self.tableView.mj_footer = footer;

看下面的gif图。

animate_image_fresh

3. 隐藏状态的刷新

// Hide the title of refresh status
footer.refreshingTitleHidden = YES;

// If does have not above method,then use 
footer.stateLabel.hidden = YES;

看下面的gif图。

hide_status_fresh

4. 全部加载完毕的刷新

//Become the status of NoMoreData
[footer noticeNoMoreData];

我们看下面的gif图。

all_load_fresh

5. DIY文字的刷新

// Set title
[footer setTitle:@"Click or drag up to refresh" forState:MJRefreshStateIdle];
[footer setTitle:@"Loading more ..." forState:MJRefreshStateRefreshing];
[footer setTitle:@"No more data" forState:MJRefreshStateNoMoreData];

// Set font
footer.stateLabel.font = [UIFont systemFontOfSize:17];

// Set textColor
footer.stateLabel.textColor = [UIColor blueColor];

我们看下面的gif图。

DIY_title_fresh

6. 隐藏footer的刷新

//Hidden current control of the pull to refresh
self.tableView.mj_footer.hidden = YES;

我们看gif图。

hide_after_loadall

7. 自动回弹的刷新

self.tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)];

我们看一下gif图。

autoback_fresh

8. 图片自动回弹的刷新

MJRefreshBackGifFooter *footer = [MJRefreshBackGifFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)];

// Set the normal state of the animated image
[footer setImages:idleImages forState:MJRefreshStateIdle];

//  Set the pulling state of animated images(Enter the status of refreshing as soon as loosen)
[footer setImages:pullingImages forState:MJRefreshStatePulling];

// Set the refreshing state of animated images
[footer setImages:refreshingImages forState:MJRefreshStateRefreshing];

// Set footer
self.tableView.mj_footer = footer;

我们看下下面gif图。

autoback_image_fresh

9.自定义控件的自动刷新

self.tableView.mj_footer = [MJDIYAutoFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)];
// Implementation reference to MJDIYAutoFooter.h和MJDIYAutoFooter.m

下面我们看gif图。

autofresh_control_fresh

10.自定义控件的自动回弹刷新

 self.tableView.mj_footer = [MJDIYBackFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)];
// Implementation reference to MJDIYBackFooter.h和MJDIYBackFooter.m

我们看下gif图。

autoback_control_fresh

11.上下拉刷新

// The drop-down refresh
self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
   //Call this Block When enter the refresh status automatically 
}];

// The pull to refresh
self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
   //Call this Block When enter the refresh status automatically
}];

我们看一下下边的gif图。

up_down_fresh

12.webview下拉刷新

//Add the control of The drop-down refresh

self.webView.scrollView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
   //Call this Block When enter the refresh status automatically
}];

我们看一下下面的gif图。

webview_fresh

我的一个非常简单的应用举例

我利用MJRefresh非常简单的实现了上拉和下拉刷新,这只是给大家看看的例子,在例子中我利用延时模拟网络加载。希望对入门级的同学会有帮助。

先看代码结构。

代码结构

然后我们看代码。

1. AppDelegate.m

#import "AppDelegate.h"
#import "JJMainVC.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    JJMainVC *mainVC = [[JJMainVC alloc] init];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:mainVC];
    self.window.rootViewController = nav;
    [self.window makeKeyAndVisible];
        
    return YES;
}

@end

2. JJMainVC.h

#import <UIKit/UIKit.h>

@interface JJMainVC : UIViewController

@end

3. JJMainVC.m

#import "JJMainVC.h"
#import "Masonry.h"
#import "JJTableViewCell.h"
#import "MJRefresh.h"

@interface JJMainVC () <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, assign) NSInteger currentPage;

@end

@implementation JJMainVC

static NSString * const kJJMainVCReuseIdentify = @"kJJMainVCReuseIdentify";

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.title = @"MJRefresh Demo";
    
    [self setupUI];
    
}

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    
    //tableview
    [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.top.bottom.equalTo(self.view);
    }];

}

#pragma mark - Object Private Function

- (void)setupUI
{
    //tableview加载
    UITableView *tableView = [[UITableView alloc] init];
    tableView.rowHeight = 80.0;
    tableView.delegate = self;
    tableView.dataSource = self;
    [tableView registerClass:[JJTableViewCell class] forCellReuseIdentifier:kJJMainVCReuseIdentify];
    tableView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:tableView];
    self.tableView = tableView;
    
    //头部刷新控件
    tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(downFreshloadData)];
    [tableView.mj_header beginRefreshing];
    //尾部刷新控件
    tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(upFreshLoadMoreData)];
    [tableView.mj_footer beginRefreshing];
}

//下拉刷新
- (void)downFreshloadData
{
    //这里加入的是网络请求,带上相关参数,利用网络工具进行请求。我这里没有网络就模拟一下数据吧。
    //网络不管请求成功还是失败都要结束更新。
    NSLog(@"我在下拉刷新");
    
    //利用延时函数模拟网络加载
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC));
    dispatch_after(time, dispatch_get_main_queue(), ^{
        [self.tableView.mj_header endRefreshing];
    });
}

//上拉加载更多
- (void)upFreshLoadMoreData
{
    //在这里上拉加载更多,将加载的数据拼接在数据源后面就可以了。
    //利用延时函数模拟网络加载
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC));
    dispatch_after(time, dispatch_get_main_queue(), ^{
        [self.tableView.mj_footer endRefreshing];
    });

}

#pragma mark - UITableViewDelegate, UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 10;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    JJTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kJJMainVCReuseIdentify forIndexPath:indexPath];
    
    return cell;
}

@end

4.  JJTableViewCell.h

#import <UIKit/UIKit.h>

@interface JJTableViewCell : UITableViewCell

@end

5. JJTableViewCell.m

#import "JJTableViewCell.h"
#import "Masonry.h"

@interface JJTableViewCell ()

@property (nonatomic, strong) UIImageView *avatarImageView;
@property (nonatomic, strong) UILabel *nameLabel;
@property (nonatomic, strong) UILabel *descLabel;

@end

@implementation JJTableViewCell

#pragma mark - Override Base Function

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        [self setupUI];
    }

    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    //头像
    [self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(self);
        make.left.equalTo(self).offset(20.0);
        make.width.height.equalTo(@50);
    }];
    
    //姓名
    [self.nameLabel sizeToFit];
    [self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.avatarImageView.mas_right).offset(20.0);
        make.top.equalTo(self.avatarImageView);
    }];
    
    //介绍
    [self.descLabel sizeToFit];
    [self.descLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.nameLabel);
        make.bottom.equalTo(self.avatarImageView);
    }];

}

#pragma mark - Object Private Function

- (void)setupUI
{
    //头像
    UIImageView *avatarImageView = [[UIImageView alloc] init];
    avatarImageView.image = [UIImage imageNamed:@"Snip20170430_5.png"];
    [self.contentView addSubview:avatarImageView];
    self.avatarImageView = avatarImageView;

    //姓名
    UILabel *nameLabel = [[UILabel alloc] init];
    nameLabel.text = @"香格里拉";
    nameLabel.textColor = [UIColor blueColor];
    nameLabel.font = [UIFont systemFontOfSize:14.0];
    [self.contentView addSubview:nameLabel];
    self.nameLabel = nameLabel;

    //介绍
    UILabel *descLabel = [[UILabel alloc] init];
    descLabel.text = @"这是一个迷人的地方,可以去看看~~~";
    descLabel.textColor = [UIColor redColor];
    descLabel.font = [UIFont systemFontOfSize:14.0];
    [self.contentView addSubview:descLabel];
    self.descLabel = descLabel;

}

@end

然后我们看下边的gif图。

simple_refresh_demo

  这个实现很简单,给初学者一个入门的看看效果,后续我有时间会加大难度,希望对大家有帮助,谢谢大家,下面给出github地址

后记

今天就写这么多了,午饭都没吃,该去吃饭了,谢谢大家,未完,待续~~~。

大海
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容