EGOTableViewPullRefresh下拉原理及代码详解

在移动应用开发中,无论是Android还是IOS应用,经常可以看到下拉列表松开后自动刷行数据,在IOS中,使用下拉刷新UITableView中的数据用的非常多,最典型的就是新浪微博的客户端,使用下拉的形式来更新最新的微博信息。


首先请点击下载源码,下载完成后里面有个Demo是可以直接运行的Xcode工程,然后就是这个开源项目的源码,如何使用可以参照Demo,这个EGOTableViewPullRefresh我修改了一部分,并添加了一些注释,主要是支持了中英文版本,原生的只支持英文,我添加了中英文支持,然后就是刷新时间的格式,修改后的格式更直观,原生的是使用SDK自带的时间格式。


这时我第一次写博客,写的不好请见谅哈!


开始进入正题。。。。。

这次主要讲解EGOTableViewPullRefresh下拉的实现原理,并对EGOTableViewPullRefresh源代码进行讲解,至于如何使用EGOTableViewPullRefresh,可以参考我上传的Demo。

UITableView继承之UIScrollView,所以利用UIScrollView滚动的位置属性contentOffset,获取用户下拉的位置contentOffset.y,通过计算用户下拉了多少,来实现下拉刷新的功能。

首先看看下拉列表的组成部分,下拉列表就是UITableView了,在UITableView中添加一个子View,用来显示下拉刷新的状态,我把它叫做HeaderView,HeaderView初始化frame的位置是{0,-60,0,60},所以正常情况下我们看不到HeaderView,当用户下拉列表时HeaderView就会显示出来。 第二张图的数值表示的是初始化的时候,不是下拉时的数值,为了方便看到HeaderView,便于理解,所以把列表下拉后标注数值。



UITableView在顶部时UIScrollView的contentoffset.y=0,在用户下拉滑动列表时,

contentoffset.y为负数增大,

当contentoffset.y <= -65时表示HeaderView已经完全显示出来了,此时HeaderView便显示“松开刷新”

,并把下拉的箭头图标向上。


此时若用户松开手,停止下拉,HeaderView的状态就改为等待数据的状态,如下图所示(下图的contentoffset.y=0标错了,是=-60)



以上就是顶部下拉刷新数据的原理了,根据这个原理也就不难写出底部上拉刷新数据的实现了。


下面我们来分析下EGOTableViewPullRefresh的代码。

EGOTableViewPullRefresh的代码结构


EGORefreshTableHeaderView.h的代码

#import 
   
   
    
    

//下拉状态
typedef enum{
    EGOOPullRefreshPulling = 0,
	EGOOPullRefreshNormal,
	EGOOPullRefreshLoading,	
} EGOPullRefreshState;

@protocol EGORefreshTableHeaderDelegate;
@interface EGORefreshTableHeaderView : UIView {
    
	id _delegate;
	EGOPullRefreshState _state;//下拉状态

	UILabel *_lastUpdatedLabel;//显示最后更新的时间
	UILabel *_statusLabel;//显示下拉状态
	CALayer *_arrowImage;//显示下拉图标
	UIActivityIndicatorView *_activityView;//等待指示器
	

}

@property(nonatomic,assign) id 
    
    
     
      delegate;

- (id)initWithFrame:(CGRect)frame arrowImageName:(NSString *)arrow textColor:(UIColor *)textColor;

//刷新最后更新时间
- (void)refreshLastUpdatedDate;

//UIScrollView 滑动时调用该方法
- (void)egoRefreshScrollViewDidScroll:(UIScrollView *)scrollView;

//UIScrollView 停止拖拽时调用该方法
- (void)egoRefreshScrollViewDidEndDragging:(UIScrollView *)scrollView;

//数据载入完成时调用此方法
- (void)egoRefreshScrollViewDataSourceDidFinishedLoading:(UIScrollView *)scrollView;

@end
//下拉委托
@protocol EGORefreshTableHeaderDelegate
//指示开始刷新数据
- (void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView*)view;
//是否正在载入数据
- (BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView*)view;
@optional
//返回最后更新时间
- (NSDate*)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView*)view;
@end
    
    
   
   


EGORefreshTableHeaderView.m的代码

#define TEXT_COLOR     [UIColor colorWithRed:87.0/255.0 green:108.0/255.0 blue:137.0/255.0 alpha:1.0]
#define FLIP_ANIMATION_DURATION 0.18f


@interface EGORefreshTableHeaderView (Private)

//设置下拉状态
- (void)setState:(EGOPullRefreshState)aState;

@end

@implementation EGORefreshTableHeaderView

@synthesize delegate=_delegate;


- (id)initWithFrame:(CGRect)frame arrowImageName:(NSString *)arrow textColor:(UIColor *)textColor  {
    if((self = [super initWithFrame:frame])) {
		//设置UIView为的缩放模式
		self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
		self.backgroundColor = [UIColor colorWithRed:226.0/255.0 green:231.0/255.0 blue:237.0/255.0 alpha:1.0];

        //初始化   用来显示最后更新日期的控件
		UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, frame.size.height - 30.0f, self.frame.size.width, 20.0f)];
		label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
		label.font = [UIFont systemFontOfSize:12.0f];
		label.textColor = textColor;
		label.shadowColor = [UIColor colorWithWhite:0.9f alpha:1.0f];
		label.shadowOffset = CGSizeMake(0.0f, 1.0f);
		label.backgroundColor = [UIColor clearColor];
		label.textAlignment = UITextAlignmentCenter;
		[self addSubview:label];
		_lastUpdatedLabel=label;
		[label release];
		
        //初始化   用来显示下拉状态的控件
		label = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, frame.size.height - 48.0f, self.frame.size.width, 20.0f)];
		label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
		label.font = [UIFont boldSystemFontOfSize:13.0f];
		label.textColor = textColor;
		label.shadowColor = [UIColor colorWithWhite:0.9f alpha:1.0f];
		label.shadowOffset = CGSizeMake(0.0f, 1.0f);
		label.backgroundColor = [UIColor clearColor];
		label.textAlignment = UITextAlignmentCenter;
		[self addSubview:label];
		_statusLabel=label;
		[label release];
		
        
        //初始化 用来显示指示图标的图层
		CALayer *layer = [CALayer layer];
		layer.frame = CGRectMake(25.0f, frame.size.height - 65.0f, 30.0f, 55.0f);
		layer.contentsGravity = kCAGravityResizeAspect;//图像显示模式
		layer.contents = (id)[UIImage imageNamed:arrow].CGImage;
		
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
		if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
			layer.contentsScale = [[UIScreen mainScreen] scale];
		}
#endif
		
		[[self layer] addSublayer:layer];
		_arrowImage=layer;
		
        //初始化  等待指示器
		UIActivityIndicatorView *view = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
		view.frame = CGRectMake(25.0f, frame.size.height - 38.0f, 20.0f, 20.0f);
		[self addSubview:view];
		_activityView = view;
		[view release];
		
		
        //初始化下拉状态
		[self setState:EGOOPullRefreshNormal];
		
    }
	
    return self;
	
}

- (id)initWithFrame:(CGRect)frame  {
  return [self initWithFrame:frame arrowImageName:@"blueArrow.png" textColor:TEXT_COLOR];
}

#pragma mark -
#pragma mark Setters

//更新最后刷新时间
- (void)refreshLastUpdatedDate {
	
	if ([_delegate respondsToSelector:@selector(egoRefreshTableHeaderDataSourceLastUpdated:)]) {
		
        //通过委托egoRefreshTableHeaderDataSourceLastUpdated:返回时间,并格式化时间
        
		NSDate *date = [_delegate egoRefreshTableHeaderDataSourceLastUpdated:self];
		
		[NSDateFormatter setDefaultFormatterBehavior:NSDateFormatterBehaviorDefault];
		NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
//		[dateFormatter setDateStyle:NSDateFormatterShortStyle];
//		[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
        [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

        //更新最后显示的时间
        NSString *lastUpdated = NSLocalizedString(@"last updated", @"");
		_lastUpdatedLabel.text = [NSString stringWithFormat:@"%@ %@",lastUpdated, [dateFormatter stringFromDate:date]];
		
        //保存最后刷新的时间,以便下次打开应用的时候还有
        [[NSUserDefaults standardUserDefaults] setObject:_lastUpdatedLabel.text forKey:@"EGORefreshTableView_LastRefresh"];
		[[NSUserDefaults standardUserDefaults] synchronize];
		
	} else {
		
		_lastUpdatedLabel.text = nil;
		
	}

}

//设置下拉状态
- (void)setState:(EGOPullRefreshState)aState{
	
	switch (aState) {
		case EGOOPullRefreshPulling://如果是下拉状态,则更新_statusLabel内容,并把下拉图标旋转180度
			
//			_statusLabel.text = NSLocalizedString(@"Release to refresh...", @"Release to refresh status");
            _statusLabel.text = NSLocalizedString(@"release to refresh", @"");
			[CATransaction begin];
			[CATransaction setAnimationDuration:FLIP_ANIMATION_DURATION];
			_arrowImage.transform = CATransform3DMakeRotation((M_PI / 180.0) * 180.0f, 0.0f, 0.0f, 1.0f);
			[CATransaction commit];
			
			break;
		case EGOOPullRefreshNormal://普通状态,恢复默认值
			
			if (_state == EGOOPullRefreshPulling) {
				[CATransaction begin];
				[CATransaction setAnimationDuration:FLIP_ANIMATION_DURATION];
				_arrowImage.transform = CATransform3DIdentity;//把transform设为默认值
				[CATransaction commit];
			}
			
//			_statusLabel.text = NSLocalizedString(@"Pull down to refresh...", @"Pull down to refresh status");
            _statusLabel.text = NSLocalizedString(@"pull down to refresh", @"");
			[_activityView stopAnimating];
			[CATransaction begin];
			[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; 
			_arrowImage.hidden = NO;
			_arrowImage.transform = CATransform3DIdentity;
			[CATransaction commit];
			
			[self refreshLastUpdatedDate];
			
			break;
            
		case EGOOPullRefreshLoading://如果是下拉状态,则显示为载入中,显示等待指示器,并隐藏箭头
			
//			_statusLabel.text = NSLocalizedString(@"Loading...", @"Loading Status");
            _statusLabel.text = NSLocalizedString(@"loading", @"");
			[_activityView startAnimating];
			[CATransaction begin];
			[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; 
			_arrowImage.hidden = YES;
			[CATransaction commit];
			
			break;
		default:
			break;
	}
	
	_state = aState;
}


#pragma mark -
#pragma mark ScrollView Methods

//UIScrollView 滑动时调用该方法
- (void)egoRefreshScrollViewDidScroll:(UIScrollView *)scrollView {	
	
	if (_state == EGOOPullRefreshLoading) {//如果正在载入中,则判断如果是上拉则不改变scrollView,如果是下拉,则保持HeaderView在顶部
		
		CGFloat offset = MAX(scrollView.contentOffset.y * -1, 0);
		offset = MIN(offset, 60);
		scrollView.contentInset = UIEdgeInsetsMake(offset, 0.0f, 0.0f, 0.0f);
		
	} else if (scrollView.isDragging) {//scrollView停止拖拽时计算下拉状态
		
		BOOL _loading = NO;
        //委托egoRefreshTableHeaderDataSourceIsLoading:返回用户载入数据的情况
		if ([_delegate respondsToSelector:@selector(egoRefreshTableHeaderDataSourceIsLoading:)]) {
			_loading = [_delegate egoRefreshTableHeaderDataSourceIsLoading:self];
		}
		
        //如果现在时上拉状态,并且没有数据在载入中,则恢复默认状态
		if (_state == EGOOPullRefreshPulling && scrollView.contentOffset.y > -65.0f && scrollView.contentOffset.y < 0.0f && !_loading) {
			[self setState:EGOOPullRefreshNormal];
		} //如果当前为默认状态,下拉大于65,并且不是在载入数据状态,则设置为Pull状态
        else if (_state == EGOOPullRefreshNormal && scrollView.contentOffset.y < -65.0f && !_loading) {
			[self setState:EGOOPullRefreshPulling];
		}
		if (scrollView.contentInset.top != 0) {
			scrollView.contentInset = UIEdgeInsetsZero;
		}
		
	}
	
}

//UIScrollView 停止拖拽时调用该方法
- (void)egoRefreshScrollViewDidEndDragging:(UIScrollView *)scrollView {
	
	BOOL _loading = NO;
	if ([_delegate respondsToSelector:@selector(egoRefreshTableHeaderDataSourceIsLoading:)]) {
		_loading = [_delegate egoRefreshTableHeaderDataSourceIsLoading:self];
	}
	
    //如果还没有载入数据,则通知载入数据
	if (scrollView.contentOffset.y <= - 65.0f && !_loading) {
		
		if ([_delegate respondsToSelector:@selector(egoRefreshTableHeaderDidTriggerRefresh:)]) {
			[_delegate egoRefreshTableHeaderDidTriggerRefresh:self];
		}
		
		[self setState:EGOOPullRefreshLoading];
		[UIView beginAnimations:nil context:NULL];
		[UIView setAnimationDuration:0.2];
		scrollView.contentInset = UIEdgeInsetsMake(60.0f, 0.0f, 0.0f, 0.0f);
		[UIView commitAnimations];
		
	}
	
}

//数据载入完成是调用此方法
- (void)egoRefreshScrollViewDataSourceDidFinishedLoading:(UIScrollView *)scrollView {	
	
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationDuration:.3];
	[scrollView setContentInset:UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f)];
	[UIView commitAnimations];
	
	[self setState:EGOOPullRefreshNormal];

}


#pragma mark -
#pragma mark Dealloc

- (void)dealloc {
	
	_delegate=nil;
	_activityView = nil;
	_statusLabel = nil;
	_arrowImage = nil;
	_lastUpdatedLabel = nil;
    [super dealloc];
}


@end


这是我发的处女文章,写的不好请见谅哈,如果觉得还可以,就转载下哈。
http://blog.csdn.net/kqjob

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值