OSChina_IOS版客户端笔记(三)_列表数据加载

       犹豫着是不是要将今天讲的"列表数据加载"也和整合到昨天写的"简单框架"上,又看了项目的本地缓存机制。觉得本地缓存好像有点问题,也有可能是自己没看仔细看吧,只缓存了第一页数据到UserDefault,而且没有进行读取(我们现在iOS版项目是参考了OSChina的Android版的缓存机制,整页整页的缓存数据,而且是缓存到本地disk)。感觉这个OSChina的iOS版好像有些地方不是很完善,担心自己后面越写越乱。于是决定干脆就写成一个单独的Demo吧,这样即使不联系上下文也可以看得懂。

       看代码时学到了一点,在OC中,单例需要覆盖allocWithZone方法,因为alloc会调用allocWithZone,如果有人没有覆盖重写allocWithZone,那么当他直接调用allocWithZone来创建对象时得到的对象仍然不是单例的。

      列表数据加载,主要由上拉加载更多和下拉在线更新组成。

      上拉加载更多

       这个还是比较简单的,就是判断服务器数据是否已经加载完成了,如果是的话列表最后一项显示"已加载全部数据",否则显示一个"加载更多"的按钮,单击就可以请求数据,进而刷新列表。这里OSChina写了一个自定义的LoadingCell(继承自UITableViewCell,包含了一个UILabel和UIActivityIndicatorView)。这个LoadingCell被一个单例对象所管理。不过我并没有按它上面写的来实现,因为我觉得将这个“加载更多”列表项写成单例的比较绕,试了一些时间后,还是打算自己写一个吧,这样自己比较清楚。

       我这里定义了一个NSObject的子类LastCell,其中持有一个UITableViewCell对象,并向外开放了几个方法(normal,loading,loadingFinished)来允许外部对象操作UITableView的显示效果。

       LastCell.h:

#import <Foundation/Foundation.h>

//一个NSObject的子类,持有一个UITableViewCell的对象引用,并向外开放几个方法(normal,loading,loadingFinished)来控制当前的显示状态
@interface LastCell : NSObject

@property (nonatomic,strong) IBOutlet UITableViewCell *cell;

//不同的状态对应的不同文本描述
@property (nonatomic,strong) NSString *normalStr;
@property (nonatomic,strong) NSString *loadingStr;
@property (nonatomic,strong) NSString *loadingFinishedStr;

//初始化方法,设置状态文本
-(id)initWithNormalStr:(NSString*)_normalStr andLoadingStr:(NSString*)_loadingStr andLoadingFinishedStr:(NSString*)_loadingFinishedStr;

-(void)normal; //"加载更多"
-(void)loadingFinished; //"数据已全部加载完成"
-(void)loading; //"数据加载中"

@end
       LastCell.m:
#import "LastCell.h"

@implementation LastCell

@synthesize cell;
@synthesize loadingStr;
@synthesize loadingFinishedStr;
@synthesize normalStr;

UILabel *label;
UIActivityIndicatorView *indicator;

-(id)initWithNormalStr:(NSString *)_normalStr andLoadingStr:(NSString *)_loadingStr andLoadingFinishedStr:(NSString *)_loadingFinishedStr {
    loadingFinishedStr = _loadingFinishedStr;
    loadingStr = _loadingStr;
    normalStr = _normalStr;
    
    cell = [[UITableViewCell alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
    label = [[UILabel alloc]initWithFrame:CGRectMake(100, 0, 200, 20)];
    indicator.frame = CGRectMake(0, 20, 20, 20);
    indicator.center = CGPointMake(200, 20);
    indicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    label.center = cell.center;
    [cell addSubview:label];
    [cell addSubview:indicator];
    
    [self normal];
    
    return self;
}

//正在加载数据中状态
-(void)loading {
    [indicator startAnimating];
    [label setText:loadingStr];
}

//加载完全部数据状态
-(void)loadingFinished {
    [indicator stopAnimating];
    [label setText:loadingFinishedStr];
}

//加载更多状态
-(void)normal {
    [indicator stopAnimating];
    [label setText:normalStr];
}

@end

       上面的逻辑相较于OSChina更简单一点,只需要将LastCell.cell添加到UITableView中,然后更加UITableView的状态来调用LastCell的normal,loading,loadingFinished等方法来控制LastCell.cell的显示即可。

       下拉刷新数据

       下拉刷新数据使用了第三方的一个组件EGORefreshTableHeaderView来实现,将EGORefreshTableHeaderView添加到UITableView上(这样它就可以随UITableView滚动而滚动了)。并实现EGORefreshTableHeaderDelegate中的几个方法:

//触发数据刷新事件,此时EGORe...View开始动画,我们可以在此时进行数据请求
- (void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView *)view
{
    //开始请求数据,注意EGORe...View的动画,应该在数据请求完成后手动关闭,即调用egoRefreshScrollViewDataSourceDidFinishedLoading方法即可
    [self reloadTableViewDataSource];
}

//与最近更新时间有关的方法
- (NSDate *)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView *)view
{
    return [NSDate date];
}

//用来查询当前刷新状态的方法
- (BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView *)view
{
    return _reloading;
}
       再简单的介绍一下EGORefreshTableHeaderView的方法调用流程:

      1.拖动列表带动EGORe...View,使其触发egoRefreshTableHeaderDidTriggerRefresh:方法,在该方法中,我们可以调用自己请求服务器数据的方法

       2.在请求完服务器数据后,调用EGORe...View的egoRefreshScrollViewDataSourceDidFinishedLoading方法手动调用关闭EGORe...View的动画。

       3、重载其他两个方法egoRefreshTableHeaderDataSourceLastUpdatedegoRefreshTableHeaderDataSourceIsLoading。
       这里主界面MyViewController.h

#import <UIKit/UIKit.h>
#import "EGORefreshTableHeaderView.h"
@interface MyViewController : UIViewController
<UITableViewDelegate,UITableViewDataSource,EGORefreshTableHeaderDelegate> {
    EGORefreshTableHeaderView *_refreshHeaderView;
    BOOL _reloading;//只是RefreshHeaderView的加载状态
}

@property (nonatomic,strong) IBOutlet UITableView *tableView;

@property (nonatomic,strong) NSMutableArray *tableData;

//下拉刷新
- (void)reloadTableViewDataSource;
- (void)doneLoadingTableViewData;
@end
       MyViewController.m
#import "MyViewController.h"
#import "LastCell.h"
@interface MyViewController ()

@end

@implementation MyViewController
@synthesize tableData;
@synthesize tableView;

NSMutableArray *serverData; //模拟服务器数据
NSInteger currPage; //当前页数标记,从1开始
NSInteger pageSize; //每页包含的数据条数

BOOL isFinishedLoad; //表示是否已经加载完服务器上的数据,用来控制列表最后一项的显示

LastCell *lastCell;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    
    //初始化参数
    isFinishedLoad = NO;
    pageSize = 20;
    currPage = 1;
    tableData = [[NSMutableArray alloc]initWithCapacity:20];
    serverData = [[NSMutableArray alloc]
                  initWithCapacity:20];
    
    //初始化服务器模拟数据,添加随机数数据,便于在下拉刷新时加以区分
    for(int i=0;i<36;i++) {
        [serverData insertObject:[NSString stringWithFormat:@"item%d,data:%d" ,i,((arc4random() % 100) + 1)] atIndex:i];
    }
    
    //添加RefreshTableHeaderView到UITableView上,使他随UITableView一起滚动
    if (_refreshHeaderView == nil) {
        EGORefreshTableHeaderView *view = [[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, -320.0f, self.view.frame.size.width, 320)];
        view.delegate = self;
        [self.tableView addSubview:view];
        _refreshHeaderView = view;
    }
    [_refreshHeaderView refreshLastUpdatedDate];


    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    tableView.delegate = self;
    tableView.dataSource = self;
    
    lastCell = [[LastCell alloc]initWithNormalStr:@"加载更多" andLoadingStr:@"数据加载中" andLoadingFinishedStr:@"数据已全部加载完成"];

    //请求服务器数据
    [self performSelector:@selector(getDataFromServe) withObject:nil
     afterDelay:0.2];
}

/**
 * @brief
 * 根据当前数据页标记从服务器请求数据
 */
-(void)getDataFromServe {

    [lastCell loading];
    if([serverData count]>= currPage*pageSize) {
        //1.请求服务器数据
        for(int i=(currPage-1)*pageSize;i<(currPage * pageSize);i++) {
            [tableData insertObject:[serverData objectAtIndex:i] atIndex:i];
        }
        isFinishedLoad = NO;
    } else {
        //已加载全部服务器数据
        //1.请求服务器数据
        for(int i=(currPage-1)*pageSize;i<[serverData count];i++) {
            [tableData insertObject:[serverData objectAtIndex:i] atIndex:i];
        }
        isFinishedLoad = YES;
    }
    currPage ++;

    //为了模拟网络请求的效果添加了1.2秒的延时效果
    [self performSelector:@selector(hideForWhat) withObject:nil afterDelay:1.2];
   
}

//使用performSelector添加延时,否则看不到IndicatorView的效果
-(void)hideForWhat {
    if(isFinishedLoad) {
        [lastCell loadingFinished];
    } else {
        [lastCell normal];
    }
    //2.刷新列表
    [tableView reloadData];
    [self doneLoadingTableViewData];
}


#pragma mark - UITableView DataSource
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [tableData count] + 1; //这里的"+1"是针对列表最后一项"加载更多"/"已完成加载"
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    if(indexPath.row < [tableData count]) {
        NSString *cellTag = @"cellTag";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellTag];
        if(cell == nil) {
            cell = [[UITableViewCell alloc]init];
        }
        UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(30, 0, 120, 40)];
        
        [label setText:[tableData objectAtIndex:indexPath.row]];
        [cell addSubview:label];
        return cell;
    } else { //最后一项,加载更多
        
        return lastCell.cell;
    }
}

#pragma mark - UITableView Delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"didSelected");
    if(indexPath.row >= [tableData count]) {
        
        if(!isFinishedLoad) { //判断,避免重复加载
            [self performSelector:@selector(getDataFromServe) withObject:nil afterDelay:0.4];
        }
    }
}

//完成下拉刷新动作,隐藏RefreshHeaderView
- (void)doneLoadingTableViewData
{
    _reloading = NO;
    [_refreshHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:self.tableView];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    [_refreshHeaderView egoRefreshScrollViewDidScroll:scrollView];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    [_refreshHeaderView egoRefreshScrollViewDidEndDragging:scrollView];
}

-(void)reloadTableViewDataSource {
    _reloading = YES;
    [serverData removeAllObjects];
    [tableData removeAllObjects];
    //初始化服务器模拟数据,添加随机数数据,便于在下拉刷新时加以区分
    for(int i=0;i<36;i++) {
        [serverData insertObject:[NSString stringWithFormat:@"item%d,data:%d" ,i,((arc4random() % 100) + 1)] atIndex:i];
    }
    
    currPage = 1;
    [self getDataFromServe];
}

//触发数据刷新事件,此时EGORe...View开始动画,我们可以在此时进行数据请求
- (void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView *)view
{
    //开始请求数据,注意EGORe...View的动画,应该在数据请求完成后手动关闭,即调用egoRefreshScrollViewDataSourceDidFinishedLoading方法即可
    [self reloadTableViewDataSource];
}

//与最近更新时间有关的方法
- (NSDate *)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView *)view
{
    return [NSDate date];
}

//用来查询当前刷新状态的方法
- (BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView *)view
{
    return _reloading;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
      因为模拟了服务器数据,所以代码可能有点长。下面是运行效果

 




       界面有些粗糙,但是基本实现了下拉刷新和上拉加载更多的功能

       工程地址:http://download.csdn.net/detail/u011638883/6615227

       O啦~~~

       转载请求保留出处:http://blog.csdn.net/u011638883/article/details/16938409

       谢谢!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值