1.项目需求
- 自定义UITableView的类似Android的ListView的adapter
- 实现UITableView下拉刷新和上拉加载功能
2.功能分析
对于Adapter:
- 定义 _TBaseAdapter类 实现 UITableView必备的两个Delegate
- 为了简便和减少冗余代码,定义_TBaseAdapterDelegate代理来实现数据的赋值填充和对应的UITableViewCell文件的获取(类似于Android的
getView()
和getView对应的item的布局文件) - 为_TBaseAdapter提供分页功能,定义变量 nowPage表示当前页码
- 使用一个可变数组
NSMutableArray *listData
为UITableView提供数据(类似于Android的List<?> listData
) - 提供对应的设置数据和页码的方法
对于下拉刷新和上拉加载更多:
- 上下拉控件对应两个类型,各三种状态:
typedef enum{
PullViewHead,//头部
PullViewFoot//尾部
} PullKind;
typedef enum{
PullStatusReady,//准备释放状态
PullStatusAction,//正在处理状态
PullStatusNone,//无动作状态
} PullStatus;
三种状态对应的转换关系为:
- 未松开手指
- 松开手指
- 其他
通过监听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;
}
}
- 实现 _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";
}
- 需要在这里调用 updatePullViewFrame 否则可能 头和尾部的视图显示不正确
-(void)viewDidAppear:(BOOL)animated{
[_adpter updatePullViewFrame ];//更新视图Frame
}
- 初始化并设置数据
- (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);
}
运行时截图: