[BS-23] AFN網絡請求上拉/下拉刷新的細節問題總結

上拉/下拉刷新的細節問題總結

1.若是導航欄有透明色,則也須要設置header自動改變透明度php

   self.tableView.mj_header.automaticallyChangeAlpha = YES; //容許自動改變透明度html

2. 下拉刷新必須手動調用[self.tableView.mj_header beginRefreshing];纔開始刷新,下拉刷新只要用戶滾動到最下方,自動開始加載更多。api

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"]];this

分析機理:atom

若是第二次下拉,返回的數據會將整個self.topics數據覆蓋掉,至關於先清空容器中數據,而後再向容器中加入新數據。若是採用self.topics=nil;這樣的方式清空,堆中的原來的模型數組(容器)因無強引用就會銷燬。而後再調用 [self.topics addObject: dict];因self.topics此前爲nil,就會懶加載從新開闢一塊內存用來充當容器。這樣將原來的罐子摔爛,買個新罐子裝東西的作法實屬浪費,這樣加大系統的負荷,效率沒有前者高。spa

 

5.經過thisPage = self.page+1來加載服務器數據的細節控制(紅色爲核心控制代碼code

#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

若有錯誤,歡迎隨時指正!

相關文章
相關標籤/搜索