IOS 菜鸟笔记 之 项目 三

1.项目需求

  1. 自定义UITableView的类似Android的ListView的adapter
  2. 实现UITableView下拉刷新和上拉加载功能

2.功能分析

对于Adapter:
  1. 定义 _TBaseAdapter类 实现 UITableView必备的两个Delegate
  2. 为了简便和减少冗余代码,定义_TBaseAdapterDelegate代理来实现数据的赋值填充和对应的UITableViewCell文件的获取(类似于Android的getView()和getView对应的item的布局文件)
  3. 为_TBaseAdapter提供分页功能,定义变量 nowPage表示当前页码
  4. 使用一个可变数组NSMutableArray *listData为UITableView提供数据(类似于Android的 List<?> listData)
  5. 提供对应的设置数据和页码的方法
对于下拉刷新和上拉加载更多:
  1. 上下拉控件对应两个类型,各三种状态:
typedef enum{
        PullViewHead,//头部
        PullViewFoot//尾部
} PullKind;
typedef enum{
    PullStatusReady,//准备释放状态
    PullStatusAction,//正在处理状态
    PullStatusNone,//无动作状态
} PullStatus;

三种状态对应的转换关系为:
- 未松开手指

Created with Raphaël 2.1.2 step1 拖动 PullStatusNone UITableView 拉动距离 超过阈值? PullStatusReady step2 yes no
Created with Raphaël 2.1.2 step2 拖动 PullStatusReady UITableView 拉动距离 小于阈值? PullStatusNone step1 yes no
  • 松开手指
Created with Raphaël 2.1.2 开始 松开手指 当前状态是否为 PullStatusReady PullStatusAction 结束 PullStatusNone yes no
  • 其他
Created with Raphaël 2.1.2 开始 PullStatusAction 调用finishAction PullStatusNone 结束

通过监听UITableView的滚动事件来决定三种状态之间的转换

2.实现方式

对于Adapter:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return _mListDatas.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    NSString * identifier = @"timShadow";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (cell == nil) {
        NSArray *array = [[NSBundle mainBundle] loadNibNamed:[_adapterDelegate getNibName] owner:self options:nil];
        cell  = [array objectAtIndex:0];
    }
    [_adapterDelegate convertDataForView:cell data:[self getItem:[indexPath row]]];
    return cell;
}


-(id) getItem:(NSInteger)index{
    return [_mListDatas objectAtIndex:index];
}

-(void)setDatas:(NSArray *)arrayDatas{
    [_mListDatas removeAllObjects];
    [_mListDatas addObjectsFromArray:arrayDatas];
    [_mTableView reloadData];
    [self updatePullViewFrame];

}

-(void)addDatas:(NSArray *)arrayDatas{
    [_mListDatas addObjectsFromArray:arrayDatas];
    [_mTableView reloadData];
    [self updatePullViewFrame];
}

-(void)clearDatas{
    [_mListDatas removeAllObjects];
    [_mTableView reloadData];
    [self updatePullViewFrame];
}

-(int)getNextPage{
    return _nowPage+1;
}

-(void)setUITableView:(UITableView *)tableview{
    [self setMTableView:tableview];
    [_mTableView setDataSource:self];
    [_mTableView setDelegate:self];
    [self setFootRefreshEnable:true];
    [self setHeadRefreshEnable:true];
    if(_isClearSplitLineWhenEmpty){
        UIView *viewView = [[UIView alloc]init];
        [viewView setBackgroundColor:[UIColor clearColor]];
        [_mTableView setTableFooterView:viewView];
        viewView = nil;
    }
}

-(instancetype) init{
    _mListDatas = [[NSMutableArray alloc]init];
    _isClearSplitLineWhenEmpty = true;//默认为 不显示
    return [super init ];
}
对于下拉刷新和上拉加载更多:
#pragma 取消加载,只有实现了 onActionFinished:kind: 代理方法的才支持,取消加载的动作可以在代理里面执行。
-(void)actionToNoneIfNeed{
    if([self.actionDelegate respondsToSelector:@selector(onActionFinished:kind:)]&&self.pullStatus==PullStatusAction){
        self.pullStatus = PullStatusReady;
        self.isNeedUpdateTime = false;
        [self finishActionAnyway];
        [self.actionDelegate onActionFinished:self kind:self.pullkind];
    }
}

#pragma 只有释放的时候才执行的方法
-(void)actionToActionIfNeed{
    if(self.pullStatus==PullStatusReady){
        [self actionToAction];
    }
}

#pragma 实际动作时执行的方法
-(void)actionToNone{
    if(self.pullStatus==PullStatusReady){
        NSLog(@"actionToNone");

        [UIView animateWithDuration:0.1f animations:^{
            float rotation = self.pullkind == PullViewFoot ? 2*M_PI :M_PI ;
            self.icon.layer.transform = CATransform3DMakeRotation( rotation, 0, 0, 1);
            [self hideActivity];
        }];
        [self setPullStatus:PullStatusNone];
        NSString *labelStr = self.pullkind == PullViewFoot ?@"继续上拉加载...":@"继续上拉刷新...";
        [self.contentLabel setText:labelStr];
    }
}

#pragma 实际动作时执行的方法
-(void)actionToReady{
    if(self.pullStatus==PullStatusNone){
        NSLog(@"actionToReady");
        [UIView animateWithDuration:0.1f animations:^{
            float rotation = self.pullkind == PullViewFoot ? M_PI : 2 * M_PI ;
            self.icon.layer.transform = CATransform3DMakeRotation(rotation, 0, 0, 1);
        }];

        [self setPullStatus:PullStatusReady];
        NSString *labelStr = self.pullkind == PullViewFoot ?@"释放开始加载更多...":@"释放开始刷新...";
        [self.contentLabel setText:labelStr];
    }
}

#pragma 实际动作时执行的方法
-(void)actionToAction{
    if(self.pullStatus == PullStatusReady){
        NSLog(@"actionToAction");
        [self showActivity];
        self.pullStatus = PullStatusAction;
        NSString *labelStr = self.pullkind == PullViewFoot ?@"正在加载...":@"正在刷新...";
        [self.contentLabel setText:labelStr];
        [self.actionDelegate onActionStarted:self kind:self.pullkind];
        [UIView  animateWithDuration:0.3f animations:^{
            if(self.pullkind == PullViewHead){
                self.scrollview.contentInset =  UIEdgeInsetsMake([self getHeight], 0, 0, 0);
            }else{
                self.scrollview.contentInset =  UIEdgeInsetsMake(0, 0, [self getHeight], 0);
            }
        }];
    }
}

#pragma 操作完成后调用。区别于 actionToNoneIfNeed
-(void)finishActionAnyway{
    NSLog(@"finishAction");
    [self.contentLabel setText:@"action"];
    self.pullStatus =PullStatusReady;//转换为预备状态,只有预备状态,才可以直接调用 actionToNone,因此,这句和下句需要连调用
    [self actionToNone];//这里需要放到 动画之前,如果放动画之后会触发其他事件,导致达不到预期效果
    [self upDateTime];
    [UIView  animateWithDuration:0.3f animations:^{
        self.scrollview.contentInset =  UIEdgeInsetsMake(0, 0, 0, 0);
    }];
}

监听UITableView的滚动事件,实现状态的转换

#pragma
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    self.isDraging = false;


    NSLog(@"scrollViewDidEndDragging");
    float offSetY = scrollView.contentOffset.y;
    float offSetBottomY = scrollView.contentSize.height >scrollView.frame.size.height?(scrollView.contentSize.height - offSetY - scrollView.frame.size.height+scrollView.contentInset.top):(-offSetY);


    float headHeight = [self.headView getHeight];
    float footHeight = [self.footView getHeight];

    //
    if(offSetY>-headHeight){//
        //    if(offSetY>-headHeight/2.0){///2.0
        NSLog(@"HeadNeed!!!!!");
        [self.headView actionToNoneIfNeed];
    }
    if(offSetBottomY>-footHeight){///2.0
        //    if(offSetBottomY>-footHeight/2.0){//
        [self.footView actionToNoneIfNeed];
    }
    //
    if(offSetY >-headHeight){
        [self.headView actionToActionIfNeed];
    }
    if(offSetBottomY > -footHeight){
        [self.footView actionToActionIfNeed];
    }

}
#pragma 用来记载是否拖拽状态
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    self.isDraging = true ;
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    float offSetY = scrollView.contentOffset.y;
    float offSetBottomY = scrollView.contentSize.height >scrollView.frame.size.height?(scrollView.contentSize.height - offSetY - scrollView.frame.size.height+scrollView.contentInset.top):(-offSetY);

    float headHeight = [self.headView getHeight];
    float footHeight = [self.footView getHeight];

    NSLog(@"%f,%f,%f",offSetY,offSetBottomY,footHeight);
    NSLog(@"%d",scrollView.isDragging);

    if(self.isDraging){
        if(offSetY < -headHeight){
            [self.headView actionToReady];
        }
        if(offSetBottomY < -footHeight ){
            [self.footView actionToReady];
        }
        if(offSetY >-headHeight){
            [self.headView actionToNone];
        }
        if(offSetBottomY > -footHeight){
            [self.footView actionToNone];
        }
    }else{
        float offSetY = scrollView.contentOffset.y;
        float offSetBottomY = scrollView.contentSize.height >scrollView.frame.size.height?(scrollView.contentSize.height - offSetY - scrollView.frame.size.height+scrollView.contentInset.top):(-offSetY);


        float headHeight = [self.headView getHeight];
        float footHeight = [self.footView getHeight];

        //
        if(offSetY>-headHeight/2.0){
            NSLog(@"HeadNeed!!!!!");
            [self.headView actionToNoneIfNeed];
        }
        if(offSetBottomY>-footHeight/2.0){
            [self.footView actionToNoneIfNeed];
        }
        //
        if(offSetY >-headHeight){
            [self.headView actionToActionIfNeed];
        }
        if(offSetBottomY > -footHeight){
            [self.footView actionToActionIfNeed];
        }
    }
}

集成方式

-(void)setFootRefreshEnable:(BOOL) isEnable{
    if(isEnable){
        _footView = [[PullView alloc ]initPullFootWithScrollView:_mTableView];
        [_mTableView addSubview:_footView];
    }else{
        _footView = nil;
    }
}

-(void)setHeadRefreshEnable:(BOOL) isEnable{
    if(isEnable){
        _headView = [[PullView alloc]initPullHeadWithScrollView:_mTableView];
        [_mTableView addSubview:_headView];
    }else{
        _headView = nil;
    }
}
  1. 实现 _TBaseAdapterDelegate
@interface ViewController : UIViewController<_TBaseAdapterDelegate,PullAction>
//刷新开始进行,比如网络的代码可以在这里调用
-(void)onActionStarted:(PullView *)view kind:(PullKind)kind{
    NSLog(@"------------onActionStarted");
}
//如果次方法不覆盖则不能取消加载,否则可以上拉取消刷新,下拉取消加载
-(void)onActionFinished:(PullView *)view kind:(PullKind)kind{
    NSLog(@"------------onActionFinished");
}

//在这里设置数据
-(void)convertDataForView:(UITableViewCell *)view data:(id)data{

}

//返回对应的TableViewCell的名称
-(NSString *)getNibName{
    return @"TableViewCell";
}
  1. 需要在这里调用 updatePullViewFrame 否则可能 头和尾部的视图显示不正确
-(void)viewDidAppear:(BOOL)animated{
    [_adpter updatePullViewFrame ];//更新视图Frame
}
  1. 初始化并设置数据
- (void)viewDidLoad {
    [super viewDidLoad];
    self.adpter = [[_TBaseAdapter alloc]init];
    [self.adpter setAdapterDelegate:self];
    [self.adpter setUITableView:self.tableView];
     _datas =   @[@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1",@"1"];
    [self.adpter  addDatas:_datas];
    [self.adpter setActionDelegate:self];
 NSLog(@"viewDidLoad:%f",_tableView.frame.size.height);
}

运行时截图:

下拉刷新正在进行
上拉加载拖拽状态

项目源码:

http://download.csdn.net/detail/x498980642/8698499

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值