[BS-23] AFN网络请求上拉/下拉刷新的细节问题总结

上拉/下拉刷新的细节问题总结

1.如果导航栏有透明色,则也需要设置header自动改变透明度

self.tableView.mj_header.automaticallyChangeAlpha = YES; //允许自动改变透明度

2. 下拉刷新必须手动调用[self.tableView.mj_header beginRefreshing];才开始刷新,下拉刷新只要用户滚动到最下方,自动开始加载更多。

3. 上拉刷新通常用的是用MJRefreshAutoNormalFooter,该控件总是紧贴最后一条数据下方。故第一次进入界面时,还没有数据,footer就会显示在最顶部,比较难看。解决的办法:因为第一次进入tableView,不管有没有从网络加载数据,它都会先调用数据源方法numberOfRowsInSection,且以后每次reloadData也会调用该方法。所以在该方法中控制footer是否隐藏最合适。

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    //第一次进来或者每次reloadData否会调一次该方法,在此控制footer是否隐藏

    self.tableView.mj_footer.hidden = (self.topics.count == 0);

    return self.topics.count;

}

4.

在上拉刷新中处理数据采用如下方法: //字典-》模型

self.topics = [WZTopic mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];

分析机理:

如果第二次下拉,返回的数据会将整个self.topics数据覆盖掉,相当于先清空容器中数据,然后再向容器中加入新数据。如果采用self.topics=nil;这样的方式清空,堆中的原来的模型数组(容器)因无强引用就会销毁。然后再调用 [self.topics addObject: dict];因self.topics此前为nil,就会懒加载重新开辟一块内存用来充当容器。这样将原来的罐子摔烂,买个新罐子装东西的做法实属浪费,这样加大系统的负荷,效率没有前者高。

5.通过thisPage = self.page+1来加载服务器数据的细节控制(红色为核心控制代码)

#import "WZWordTableViewController.h"
#import <AFNetworking.h>
#import <UIImageView+WebCache.h> //分类
#import "WZTopic.h"
#import <MJExtension.h>
#import <SVProgressHUD.h>
#import <MJRefresh.h>

@interface WZWordTableViewController ()
//服务器返回的值
@property (strong,nonatomic) NSNumber *count; //服务器返回的总数据条数
@property (strong,nonatomic) NSMutableArray *topics; //模型数组

//请求参数
@property (assign,nonatomic) NSInteger page;//发送请求时使用的页数,每次加载下一页,让该值page +1

//保存本次发送请求的参数,用于控制只处理最后一次请求的参数(如下拉不成功,又去上拉,上拉的数据显示出来后,下拉的数据才回来,此时会将表中已显示数据突然清空,只显示第一页数据)
@property (strong,nonatomic) NSDictionary *newestParams;

@end

@implementation WZWordTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.rowHeight = 50;

    //设置下拉和上拉刷新
    [self setupMJRefresh];

}

//懒加载topics的get方法,第一次调self.topics时,分配空间和初始化topics数组
- (NSMutableArray *)topics {

    if (!_topics) {
        _topics = [[NSMutableArray alloc]init];
    }

    return _topics;
}

/** 设置下拉和上拉刷新 */
- (void)setupMJRefresh {

    //设置header
    self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewTopics)];
    //允许自动改变透明度
    self.tableView.mj_header.automaticallyChangeAlpha = YES;
    //header开始刷新
    [self.tableView.mj_header beginRefreshing];

    //设置footer,用户滑到最下边就会自动启用footer刷新,故不用写开始刷新的代码
    self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreTopics)];

    //self.tableView.mj_footer.hidden = YES;//数据尚未返回,或者加载失败footer都会跑到最顶部。//改成在numberOfRows方法中控制了
}

/** 加载新的帖子 */
- (void)loadNewTopics {

    //下拉先停止footer刷新
    [self.tableView.mj_footer endRefreshing];

    //每次下拉让页码都回归到第0页
    //self.page = 0; (不在此设置,如多次下拉加载不成功,在回到底部上拉,此时页码已变为0,之前可能已是第5页了,后面又接着1,2,3...出现重复加载)

    //设置参数
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    params[@"a"] = @"list";
    NSInteger thisPage = 0; //第一次请求发送0还是1由看接口文档,此处不能直接写self.page=0; 每次请求成功后,才能将当前thisPage保存进self.page中,供下次请求使用。
    params[@"page"] = @(thisPage); //此处不能写为@(self.page),

    self.newestParams = params; //self.newestParams是全局的,永远只存储最后一次请求的参数,之前的请求参数都会被最后一次覆盖掉。

    //发送请求
    [[AFHTTPSessionManager manager] GET:@"http://api.budejie.com/api/api_open.php" parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {

        //如果不是用户最近一次发送的请求,就不处理,直接return整个AFN方法,后面failure也不会执行
        if(params != self.newestParams) return;

        //能来到这说明本次加载成功,此时将本次页码保存在self.page中,以便下次加载使用。
        self.page = thisPage;

        //解析服务器返回数据
        NSDictionary *infoDict = responseObject[@"info"];
        self.count = infoDict[@"count"]; //总帖子数可能随时会增加,每次请求都重新存储一遍

        //字典-》模型(如果第二次下拉,返回的数据会将整个self.topics数据覆盖掉,相当于先清空,再加载)
        self.topics = [WZTopic mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];//此句会在内部通过KVC将responseObject[@"list"]这个包含20个字典的数组转为包含20个topic模型的数组

        [self.tableView reloadData];//刷新表格才会显示数据

        //返回数据了就停止刷新
        [self.tableView.mj_header endRefreshing];

        //让footer显示出来(第一次让footer隐藏了)
        //self.tableView.mj_footer.hidden = NO; //改成在numberOfRows方法中控制了

    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

        //提示用户加载失败
        [SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeBlack];//第一次使用时设置就可以
        [SVProgressHUD showErrorWithStatus:@"加载失败"];

        //停止header刷新(第一次加载失败,footer就不会显示出来,不用停止footer)
        [self.tableView.mj_header endRefreshing];

    }];

}

/** 加载更多帖子 */
- (void)loadMoreTopics {

    //上拉先停止header刷新
    [self.tableView.mj_header endRefreshing];

    //设置参数
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    params[@"a"] = @"list";
    NSInteger thisPage = (self.page+1); //此处不能写成++self.page,会改变self.page的值。如果加载不成功,还得去--。每次请求成功后,才能将当前thisPage保存进self.page中,供下次请求使用。
    params[@"page"] = @(thisPage);
    self.newestParams = params; //self.newestParams是全局的,永远只存储最后一次请求的参数,之前的请求参数都会被最后一次覆盖掉。

    //发送请求
    [[AFHTTPSessionManager manager] GET:@"http://api.budejie.com/api/api_open.php" parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {

        //如果不是用户最近一次发送的请求,就不处理,直接return整个AFN方法,后面failure也不会执行
        if(params != self.newestParams) return;

        //能来到这说明本次加载成功,此时将本次页码保存在self.page中,以便下次加载使用。
        self.page = thisPage;

        //解析服务器返回数据
        NSDictionary *infoDict = responseObject[@"info"];
        self.count = infoDict[@"count"]; //总帖子数可能随时会增加,每次请求都重新存储一遍

        //字典-》模型
        NSArray *array = [WZTopic mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];//此句会在内部通过KVC将responseObject[@"list"]这个包含20个字典的数组转为包含20个topic模型的数组

        //添加模型到可变数字topics中
        [self.topics addObjectsFromArray:array];//把第二次返回的数据加到数组中

        [self.tableView reloadData];//刷新表格才会显示数据

    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

        //提示用户加载失败
        //[SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeBlack];//只需要设置一次就ok
        [SVProgressHUD showErrorWithStatus:@"加载失败"];

        //加载失败了footer停止刷新
        [self.tableView.mj_footer endRefreshing];

    }];
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    //判断footer是否隐藏,判断footer停止刷新时显示"加载更多"还是"全部数据已加载完成"
    [self checkFooterStatus];

    return self.topics.count;
}

//判断footer是否隐藏,判断footer停止刷新时显示"加载更多"还是"全部数据已加载完成"
-(void)checkFooterStatus {
    //第一次进来或者每次reloadData否会调一次该方法,在此控制footer是否隐藏
    self.tableView.mj_footer.hidden = (self.topics.count == 0);

    //用户滑到最下边就会自动启用footer刷新,现在数据回来了要footer停止刷新
    if (self.topics.count == self.count.unsignedIntegerValue) {
        [self.tableView.mj_footer endRefreshingWithNoMoreData];
    } else {
        [self.tableView.mj_footer endRefreshing];
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    //创建cell
    static NSString *const ID = @"Word";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    }

    //获取模型
    WZTopic *topic = self.topics[indexPath.row];

    //设置数据
    cell.textLabel.text = topic.name;
    cell.detailTextLabel.text = topic.text;
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:topic.profile_image] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"]];

    //返回cell
    return cell;
}

@end

6.通过最大的id来加载服务器数据的细节控制(删掉恶心的page控制代码,直接发给服务器最大id即可加载下一页,红色为核心控制代码)

#import "WZWordTableViewController.h"
#import <AFNetworking.h>
#import <UIImageView+WebCache.h> //分类
#import "WZTopic.h"
#import <MJExtension.h>
#import <SVProgressHUD.h>
#import <MJRefresh.h>

@interface WZWordTableViewController ()
//服务器返回的值
@property (strong,nonatomic) NSNumber *count; //服务器返回的总数据条数
@property (strong,nonatomic) NSMutableArray *topics; //模型数组

//保存本次发送请求的参数,用于控制只处理最后一次请求的参数(如下拉不成功,又去上拉,上拉的数据显示出来后,下拉的数据才回来,此时会将表中已显示数据突然清空,只显示第一页数据)
@property (strong,nonatomic) NSDictionary *newestParams;

@end

@implementation WZWordTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.rowHeight = 50;

    //设置下拉和上拉刷新
    [self setupMJRefresh];

}

//懒加载topics的get方法,第一次调self.topics时,分配空间和初始化topics数组
- (NSMutableArray *)topics {

    if (!_topics) {
        _topics = [[NSMutableArray alloc]init];
    }

    return _topics;
}

/** 设置下拉和上拉刷新 */
- (void)setupMJRefresh {

    //设置header
    self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewTopics)];
    //允许自动改变透明度
    self.tableView.mj_header.automaticallyChangeAlpha = YES;
    //header开始刷新
    [self.tableView.mj_header beginRefreshing];

    //设置footer,用户滑到最下边就会自动启用footer刷新,故不用写开始刷新的代码
    self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreTopics)];

    //self.tableView.mj_footer.hidden = YES;//数据尚未返回,或者加载失败footer都会跑到最顶部。//改成在numberOfRows方法中控制了
}

/** 加载新的帖子 */
- (void)loadNewTopics {

    //下拉先停止footer刷新
    [self.tableView.mj_footer endRefreshing];

    //设置参数
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    params[@"a"] = @"list";
   //params[@"maxtime"] = @""; //不设置maxtime默认加载第一页
    self.newestParams = params; //self.newestParams是全局的,永远只存储最后一次请求的参数,之前的请求参数都会被最后一次覆盖掉。

    //发送请求
    [[AFHTTPSessionManager manager] GET:@"http://api.com/api/api_open.php" parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {

        //如果不是用户最近一次发送的请求,就不处理,直接return整个AFN方法,后面failure也不会执行
        if(params != self.newestParams) return;

        //解析服务器返回数据
        NSDictionary *infoDict = responseObject[@"info"];
        self.count = infoDict[@"count"]; //总帖子数可能随时会增加,每次请求都重新存储一遍
        self.maxtime = infoDict[@"maxtime"]; //下次加载需使用

        //字典-》模型(如果第二次下拉,返回的数据会将整个self.topics数据覆盖掉,相当于先清空,再加载)
        self.topics = [WZTopic mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];//此句会在内部通过KVC将responseObject[@"list"]这个包含20个字典的数组转为包含20个topic模型的数组

        [self.tableView reloadData];//刷新表格才会显示数据

        //返回数据了就停止刷新
        [self.tableView.mj_header endRefreshing];

        //让footer显示出来(第一次让footer隐藏了)
        //self.tableView.mj_footer.hidden = NO; //改成在numberOfRows方法中控制了

    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

        //提示用户加载失败
        [SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeBlack];//第一次使用时设置就可以
        [SVProgressHUD showErrorWithStatus:@"加载失败"];

        //停止header刷新(第一次加载失败,footer就不会显示出来,不用停止footer)
        [self.tableView.mj_header endRefreshing];

    }];

}

/** 加载更多帖子 */
- (void)loadMoreTopics {

    //上拉先停止header刷新
    [self.tableView.mj_header endRefreshing];

    //设置参数
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    params[@"a"] = @"list";
    params[@"maxtime"] = self.maxtime; //由上一次返回的数据提供
    self.newestParams = params; //self.newestParams是全局的,永远只存储最后一次请求的参数,之前的请求参数都会被最后一次覆盖掉。

    //发送请求
    [[AFHTTPSessionManager manager] GET:@"http://api.com/api/api_open.php" parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {

        //如果不是用户最近一次发送的请求,就不处理,直接return整个AFN方法,后面failure也不会执行
        if(params != self.newestParams) return;

        //解析服务器返回数据
        NSDictionary *infoDict = responseObject[@"info"];
        self.count = infoDict[@"count"]; //总帖子数可能随时会增加,每次请求都重新存储一遍
        self.maxtime = infoDict[@"maxtime"]; //下次加载需使用

        //字典-》模型
        NSArray *array = [WZTopic mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];//此句会在内部通过KVC将responseObject[@"list"]这个包含20个字典的数组转为包含20个topic模型的数组

        //添加模型到可变数字topics中
        [self.topics addObjectsFromArray:array];//把第二次返回的数据加到数组中

        [self.tableView reloadData];//刷新表格才会显示数据

    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

        //提示用户加载失败
        //[SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeBlack];//只需要设置一次就ok
        [SVProgressHUD showErrorWithStatus:@"加载失败"];

        //加载失败了footer停止刷新
        [self.tableView.mj_footer endRefreshing];

    }];
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    //判断footer是否隐藏,判断footer停止刷新时显示"加载更多"还是"全部数据已加载完成"
    [self checkFooterStatus];

    return self.topics.count;
}

//判断footer是否隐藏,判断footer停止刷新时显示"加载更多"还是"全部数据已加载完成"
-(void)checkFooterStatus {
    //第一次进来或者每次reloadData否会调一次该方法,在此控制footer是否隐藏
    self.tableView.mj_footer.hidden = (self.topics.count == 0);

    //用户滑到最下边就会自动启用footer刷新,现在数据回来了要footer停止刷新
    if (self.topics.count == self.count.unsignedIntegerValue) {
        [self.tableView.mj_footer endRefreshingWithNoMoreData];
    } else {
        [self.tableView.mj_footer endRefreshing];
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    //创建cell
    static NSString *const ID = @"Word";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    }

    //获取模型
    WZTopic *topic = self.topics[indexPath.row];

    //设置数据
    cell.textLabel.text = topic.name;
    cell.detailTextLabel.text = topic.text;
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:topic.profile_image] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"]];

    //返回cell
    return cell;
}

@end    

文章系作者原创,转载请注明出处:http://www.cnblogs.com/stevenwuzheng/p/5506286.html

如有错误,欢迎随时指正!

时间: 2024-10-16 00:39:57

[BS-23] AFN网络请求上拉/下拉刷新的细节问题总结的相关文章

Android PullToRefresh上、下拉刷新

支持各种上.下拉刷新. Github: https://github.com/chrisbanes/Android-PullToRefresh Features Supports both Pulling Down from the top, and Pulling Up from the bottom (or even both). Animated Scrolling for all devices. Over Scroll supports for devices on Android v

TableView上的下拉刷新及抓获本地时间。

#import <UIKit/UIKit.h> @interface ViewController : UITableViewController{        NSMutableArray *timeArray;        UIRefreshControl *refresh;    } @property (strong,nonatomic)NSMutableArray *timeArray;@property (strong,nonatomic)UIRefreshControl *r

浅谈对MJRefresh(上)下拉刷新控件的理解

MJRefresh GitHub地址:https://github.com/CoderMJLee/MJRefresh 利用业余时间研究了一下iOS的开发,发现OC特定的语法方式吸引了我,而且iOS开发中有很多有趣的东西,正是如此,重新激起了我对开发学习的兴趣.自学过程中,知道了这个MJRefresh,MJ真乃大神也. 废话不多说,MJRefresh集成了UIView,UICollectionView,UITableView的上下拉刷新功能,而且还有自定义文字,动画等功能.针对GitHub上的源代

上、下拉电阻

单片机上拉电阻作用 加上拉电阻或下拉电阻就是从电源V+或V-端到集成电路器件输出端加装一个电阻,即直接在器件的输出脚到电源V+或V-端焊接一个电阻即可.1.上拉电阻对器件注入电流,常见的加装目的有两个:(1)提高输出电平.如TTL输出驱动COM的电平匹配,这是非常必要的.(2)加大输出驱动能力,但对于非OC或OD输出型电路其作用是有限的,如果用于驱动类似LED不加上拉或下拉电阻也是可以的,应该从负载限流电阻等方面考虑解决,如果负载比较重,应该加装输出缓冲或功率驱动电路.对于OC或OD电路,必须由

160823、ionic上拉/下拉更新数据

<!DOCTYPE html> <html ng-app="myApp"> <head> <meta charset="UTF-8"> <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no,width=device-width,height=device-height"

PullToRefreshView 上啦下拉刷新

public class PullToRefreshView extends LinearLayout { // private static final String TAG = "PullToRefreshView"; // refresh states private static final int PULL_TO_REFRESH = 2; private static final int RELEASE_TO_REFRESH = 3; private static final

c# - Winform DatagridView上显示下拉树

Winform的DatagridView是不支持下拉树的,所以需要扩展 废话不多说,直接贴代码 首先需要对comBox扩展,下拉内容变成TreeView using System.Drawing;using System.Windows.Forms;namespace WindowsApplication23{ public class ComboBoxTreeView : ComboBox { private const int WM_LBUTTONDOWN = 0x201, WM_LBUTT

翻翻git之---RecycleView的上拉,下拉刷新,样式切换,添加foot和header的强大库 RecyclerViewManager

转载请注明出处王亟亟的大牛之路 前两天写了一个上拉刷新的,今天看到个上下拉都可刷新还实现了foot 和header以及3种RecycleView样式的切换就给大家介绍下,先贴下foot部分因为实例中并没加入foot 这部图片大小 没搞好 再贴一下2个刷新的效果 因为作者是中国人,所以实现原理他写了一个很好的解释文章,这边就不罗嗦了看传送门:http://z.sye.space/2015/11/23/RecyclerViewManager/ how to use? Grade: allprojec

ios AFN 网络请求失败 failed: unacceptable content-type: text/html application/x-javascript

原因:请求类型没设 解决方法: