MJRefresh在开发中的应用

Demo1

#import "ViewController.h"
#import "Status.h"
#import "StatusCell.h"
#import "MJExtension.h"
#import "MJRefresh/MJRefresh.h"
#import "AFNetworking.h"
#import "SVProgressHUD.h"

@interface ViewController ()
/** 所有的微博模型*/
@property (nonatomic ,strong) NSMutableArray *statuses;
@property(nonatomic,assign) NSInteger page;

@end

@implementation ViewController

- (BOOL)prefersStatusBarHidden{
    return YES;
}

- (NSMutableArray *)statuses
{
    if (!_statuses) {
        _statuses = [NSMutableArray array];
    }
    return _statuses;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // self-sizing(iOS8 以后)
    // 告诉tableView所有cell的高度是自动计算的(根据设置的约束来计算)
    self.tableView.rowHeight = UITableViewAutomaticDimension;
    
    // 告诉tableView所有cell的估算高度
    self.tableView.estimatedRowHeight = 44;
    
    __weak __typeof(self) weakSelf = self;
    // 设置回调(一旦进入刷新状态就会调用这个refreshingBlock)
    self.tableView.header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
        // 将发送网络请求的方法写在这个代码块里。以后只要我们滚动tableView就会自动执行这个代码块。因为MJ在底层已经为我们实现了:如果我们滚动tableView,就会触发headerWithRefreshingBlock,执行代码块中的内容
        [weakSelf getDataWithHeader:YES];
    }];
    
    self.tableView.footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
         // 将发送网络请求的方法写在这个代码块里。以后只要我们滚动tableView就会自动执行这个代码块。因为MJ在底层已经为我们实现了:如果我们滚动tableView,就会触发headerWithRefreshingBlock,执行代码块中的内容
        [weakSelf getDataWithHeader:NO];
    }];
    
    // 自动进入刷新状态,执行headerWithRefreshingBlock中的代码块.
    // 相当于你手动下拉然后松手,就会执行headerWithRefreshingBlock中的代码块{}。
    // 如果没有这句代码,程序初始运行将不会执行代买块中的内容,所以也就不会发送网络请求,所以tableView上啥也不显示
    [self.tableView.header beginRefreshing];
}

-(void)getDataWithHeader:(BOOL)status
{
    // 如果是下拉刷新就设置当前页数为1,否则每次都+1
    if (status) {
        self.page = 1;
        // 下拉刷新清空以前的数据
        [self.statuses removeAllObjects];
    }else{
        self.page += 1;
    }
    // 创建AFN管理者
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    
    // 设置参数
    NSDictionary *params = @{@"page":@(self.page)};
    // 设置URL
    NSString *baseUrl = @"http://api.liyaogang.com/weibo/status.php";
    // 发送请求
    [manager POST:baseUrl parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
        // 解析数据
        NSMutableArray *arr = [Status mj_objectArrayWithKeyValuesArray:responseObject];
        [self.statuses addObjectsFromArray:arr];

        // 刷新表格数据
        if (arr.count != 0) {
            [self.tableView reloadData];
        }else{
            // 设置黑色的遮罩
            [SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeBlack];
            [SVProgressHUD showErrorWithStatus:@"没有更多数据了"];
        }
        // 拿到当前的下拉刷新控件,结束刷新状态
        [self.tableView.header endRefreshing];
        [self.tableView.footer endRefreshing];
        
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        // 拿到当前的下拉刷新控件,结束刷新状态
        [self.tableView.header endRefreshing];
        [self.tableView.footer endRefreshing];
        NSLog(@"%@",error);
    }];
}

#pragma mark - 数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.statuses.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"status";
    StatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
    // 传递模型数据
    cell.status = self.statuses[indexPath.row];
    return cell;
}

@end
  • 未调用beginRefreshing,程序初始运行并不会自动发送网络请求
没有beginRefreshing.gif
  • 未调用beginRefreshing,程序初始运行会自动发送网络请求


    有beginRefreshing.gif

链接密码


Demo2

#import "MJRefresh/MJRefresh.h"

#import "ViewController.h"

@interface ViewController () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, weak) UITableView *tableView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UITableView *tableView = [[UITableView alloc] init];
//    tableView.frame = CGRectMake(80, 100, 200, 300);
    tableView.frame = self.view.bounds;
    tableView.backgroundColor = [UIColor grayColor];
    //数据源
    tableView.dataSource = self;
    //代理
    tableView.delegate = self;
    
    [self.view addSubview:tableView];
    // 由于MJ的底层的作用,以后只要滚动tableView就会自动调用downNetWorkData,即使downNetWorkData写在了viewDidLoad中也没关系。
    MJRefreshGifHeader *header = [MJRefreshGifHeader headerWithRefreshingTarget:self refreshingAction:@selector(downNetWorkData)];
    [header setImages:@[[UIImage imageNamed:@"first.png"]] forState:MJRefreshStateIdle];
    [header setImages:@[[UIImage imageNamed:@"second.png"],[UIImage imageNamed:@"third.png"]] forState:MJRefreshStatePulling];
    [header setImages:@[[UIImage imageNamed:@"second.png"],[UIImage imageNamed:@"third.png"],[UIImage imageNamed:@"fourth.png"]] forState:MJRefreshStateRefreshing];
    tableView.mj_header = header;
    tableView.mj_header.automaticallyChangeAlpha = YES;
    self.tableView = tableView;
    // 程序初始运行自动调用downNetWorkData方法
    [self.tableView.mj_header beginRefreshing];
}
- (void)downNetWorkData
{
    NSLog(@"发送网络请求");
    [self getNetWorkData];
}

-(void)getNetWorkData{
    
    [self.tableView reloadData];
    
}

#pragma mark - <UITableViewDelegate>
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"contentOffset.y = %f", tableView.contentOffset.y);
    NSLog(@"contentSize.height = %f", tableView.contentSize.height);
}

#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 20;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
        cell.backgroundColor = [UIColor redColor];
    }
    cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         [self.tableView.mj_header endRefreshing];
    });
   
    return cell;
}

@end
  • 未调用beginRefreshing,程序初始运行并不会自动发送网络请求


    无beginRefreshing.gif
  • 调用beginRefreshing,程序初始运行会自动发送网络请求


    有 beginRefreshing.gif

上拉加载和下拉刷新都是全局刷新[self.tableView reloadData];,知道了这一点,这句话就非常容易理解了:下拉刷新要清空数组中之前存储的数据,上拉加载不仅不清空数组中之前存储的数据而且还要将新请求到的数据拼接到之前数据的后面。

上拉加载:即使数据库中有新数据出现,也不需要,因为我们需要的是更老的数据,在Xcode中通过网络请求参数进行区分上拉加载还是下拉刷新(URL一样,请求参数不一样),后台收到网络请求根据参数识别是上拉加载,通过php代码从数据库中拿到更老的数据返回被Xcode

下拉刷新和上拉刷新 耗费流量的阶段:发送网络请求的阶段和显示UI中的图片的阶段,但是当UI中的图片已经下载完毕之后,利用SDWebImage框架再次显示这张图片时就不需要耗费流量下载了,只需要从缓存中取出来即可。
鉴于这种情况,当cell上有10条数据时,我们此时进行上拉加载,不需要清空数组A中存储的10条数据:因为你清空之后还得发送网络请求,请求回来,这10条数据显示的位置是固定不变的,变的是执行上拉加载操作得到的更老的10条数据。只需要发送网络请求,请求到更老的10条数据拼接到数组A,这样数组A中就有20条数据了,然后利用全局刷新将20条数据按照数组A中的顺序依次显示到cell上。注意:由于TableView的重用机制,之前显示的10条数据在这20条数据里面,所以前10条直接从缓存池取出来重用即可,不需要创建cell了,而剩下的10条,也就是刚刚下拉加载的10条,由于之前没有显示过,就必须得创建10个cell显示这10条数据到cell上。鉴于这种原因:上拉加载就给用户造成了一种假象:前10条数据根本就没有发生变化,后10条数据被新添加到前10条数据的后面,但实际上这20条数据进行了都进行了全局刷新,因为前10条数据在数组A中的位置始终不变,始终在前10位,并且也没有被清空,新请求到数据还拼接到了这10条数据的后面,前10条数据在cell上能看到位置发生变化才怪!!!!!
当cell上有10条数据时,我们此时进行下拉刷新,必须清空数组A中存储的10条数据:因为有新数据要显示时,必须显示到所有数据的最前面,这种情况下数组A中存储的10条数据的位置肯定发生了变化,比如有1条新数据,那么这10条数据就会错位,之前在第1个位置上的数据会下移到第2位,之前在第10个位置上的数据会下移到第11位.唯有清空数组A重新获取10条数据才能确保新数据在第一个位置(PHP后台利用SQL语句读数据库中的数据,并将数据按时间或者id排序来将最新的数据放到数组中的第一个位置,然后返回给Xcode,然后在Xcode中对数据进行字典转模型并存进数组A中,这样数组A中的第一个数据肯定是最新的数据了)。

上拉加载和下拉刷新的细节问题【重点】:运行程序,cell上显示10条数据,此时进行上拉加载又多显示出10条数据,这样cell上一共就有20条数据了。如果我们此时将cell滚动到顶部,然后进行下拉刷新,那么之前的20条数据就会被清除,就会将这次请求到的10条新数据显示到cell上。注意:此时cell上就只有10条数据了,只有再次上拉加载才会增加数据。这也就可以百分之百的确认 下拉刷新会将之前显示到cell上的数据全部清除,只会将本次请求到的数据显示到cell上(所有的APP都是这么设计的)。

下拉刷新之所以先清空可变数组A中的数据,然后再向数组A中存储本次发送网络请求得到的数据的原因:如果不清空,那么上次和本次请求到的数据会同时存储到可变数组A中,刷新tableView就会拿取可变数组A中的数据进行显示,就会造成数据的重复加载。另外数据都是从服务器的数据库中获取的,数据库就是存储数据的地方,所以你即使清空可变数组A中存储的上次发送网络请求得到的数据 也没有关系,因为数据都是从服务器的数据库中获取的,你本次发送网络请求获取到的数据肯定是最新的数据,将最新的数据存储到可变数组A中并不带有上次获取到的数据,才能保证数据不会重复加载

清空可变数组中数据的三种方式:
方式一: [self.videoRemarkDataSource removeAllObjects];
方式二: self.videoRemarkDataSource = commentArray;
方三:self.topics=[ZBTopic mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];

着重讲一下方式二:commentArray临时可变数组中存储着本次发送网络请求得到的数据,videoRemarkDataSource可变数组是全局的属性,是要拿到cellForRowAtIndexPath:方法中用到的。通过数组赋值给数组的形式,等号左侧之前存储的数据就会被覆盖掉,这种以覆盖的形式来清空可变数组中数据的方式给人一种不是清空可变数组中数据的感觉,但是确确实实是。方式三也是数组赋值给数组的形式。

注意区分[self.videoRemarkDataSource addObjectsFromArray:commentArray];这句代码并不会将videoRemarkDataSource可变数组中之前存储的数据覆盖掉,而是会添加到可变数组中,这样本次请求到的数据和上次请求到的数据会同时存在。


在哪里对服务器的数据库中的数据进行排序?:在后台利用PHP代码+SQL语句读取数据库中的字段并进行排序,这种方式通过读的方式进行排序是最稳妥的,而不是向数据库中插入数据的时候时进行排序,这样不稳妥,一般向服务器插入数据新数据都是插入到最后一行,如果插入到第一行,很麻烦且不说,公司一般也不允许。读的方式既不会影响数据库中原始的数据的存在形式,同时能够达到排序的目的,我们何乐而不为呢?
发送网络请求必须得有数据库,因为数据都在数据库中。当我们在测试MJRefresh的上拉加载,下拉刷新时,若我们没有搭建数据库,可以在代码中模拟一个类似数据库的操作.如下代码↓ ,这样每次下拉刷新调用loadNewData时,之前存储在data可变数组中的数据依然存在,并且由于atIndex:0的作用也能保证最新的数据会显示在data数组中的最前面,最终调用cellForRowAtIndexPath:方法显示cell时,最新的数据也会显示到最前面。

#pragma mark 下拉刷新数据
- (void)loadNewData
{
    for (int i = 0; i<5; i++) {

        [self.data insertObject:[NSString stringWithFormat:@"随机数据---%d", arc4random_uniform(1000000)] atIndex:0];
    }
}


说到这里就要必要讲解一下当有数据库时,下拉刷新为啥从服务器的数据库中获取到的最新的数据会显示最前面:
向数据库中插入数据时:我们在客户端、Xcode发送的评论等数据都要发送网络请求将数据存储到数据库中,两者是通过后台的PHP语句相衔接的,通过后台的PHP语句嵌套SQL语句实现将评论等数据插入到数据库中.
获取数据库中的数据时:当我们下拉刷新时,就会发送网络请求,后台会收到请求,并根据请求中传入的参数来利用PHP语言+SQL语句读取数据库中的指定的数据,这里的SQL语句利用了排序使得获得的数据按时间或者id排序(** select * from 表 order by 时间字段 desc** 或者 ** select * from 表 order by id desc** ),经过排序之后最新的数据将会显示在最前面,然后将最新的数据返回给xcode,这就是为什么下拉刷新时最新的数据会显示最前面的原因。
熟悉一下后台代码

当有数据库时,上拉加载时为啥从服务器的数据库中获取到的数据不包括已经获取到的数据并且 会显示到已显示数据的后面:
当我们上拉加载时,就会发送网络请求,必须携带后台指定的参数,后台会根据传递的参数例如offset = 1判断是上拉加载,既然是上拉加载就拿到上次网络请求得到的最后一个数据的id,以这个id开始,向下读取数据库中的10个数据,这样就能保证上拉加载时为啥从服务器的数据库中获取到的数据不包括已经获取到的数据,但要实现从服务器的数据库中获取到的数据显示到已显示数据的后面这需要我们在Xcode中进行操作,通过[self.videoRemarkDataSource addObjectsFromArray:commentArray];代码,commentArray临时可变数组中存储着本次发送网络请求得到的数据,videoRemarkDataSource全局可变数组之前就存储着之前的获取到的数据,再加上这次本次发送网络请求得到的数据,那么videoRemarkDataSource中数组中数据顺序就是:**老数据+本次发送网络请求得到的数据(更老的数据 或者 上拉加载的数据) **
http://bbs.csdn.net/topics/390849091

[LS](https://pan.baidu.com/s/1bTpXSE 密码2bdi)

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,506评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,024评论 4 62
  • 不知道是不是因为高中学校男生太少 特别怀念初中时和自己认识接触暧昧过的男生 时常还有着那么一点点的错觉 是不是太...
    织虞阅读 658评论 0 0
  • 水天一色渔舟荡, 薄雾升腾对影双。 清唱歌谣桑梓韵, 顺江直下是仙乡。
    不惑而歌阅读 232评论 18 24
  • 行销最具威力的一种战略,就是在潜在顾客脑海中占有一个自己独享的字眼。 焦点法则,就是企业集中力量做好单类业务,从而...
    张琦香141319阅读 534评论 0 0