在IOS中,经常会使用到下拉刷新UITableView中的数据,比如新浪的客户端,我们就是使用下拉刷新方式来更新微博数据。所以这次主要是对EGORefreshTableHeaderView下拉刷新原理进行分析。
在EGORefreshTableHeaderView中只支持英文,通过修改文中_statusLabel.text的内容,可以支持这种那个英文,例如_statusLabel.text = NSLocalizedString(@"下拉刷新",@"Release to refresh status");
UITableView继承于UIScrollView,所以可以使用UIScrollView中y的位移量contentOffset.y 计算用户当前下拉的位置,从而判断是否开始刷新数据。
首先在TableView视图中,我们首先给视图添加一个view用来显示下拉刷新的状态,我们可以把它叫做refreshHeardView。在普通状态下,我们看不到refreshHeardView,因此我们可以将其frame设置到屏幕以外;当用户下拉列表时refreshHeardView就会一点一点显示出来。
普通状态下,contentOffset.y = 0;当用户下拉时,contentOffsetu6.y的值不断变小,refreshHeardView上显示的箭头朝下,内容为:下拉加载.......;当contentOffset.y <= -65 -offsetY(#define offsetY (refresh_ios7?64.0:0.0))时,refreshHeardView上控件就会完全显示出来,此时箭头朝上,变为:加载新微博; 此时放手,refreshHeardView不上内容变为:正在加载...........;效果图如 下图所示
根据上面原理就不难理解下面代码中的原理 :
首先是在初始化的时候创建我们所需要的视图view中的各个控件。_lastUpdateLabel用来显示时间;_statusLabel用来显示下拉的状态;_arrowImage用来显示箭头;_activityView是指示等待器,当加载数据是显示。
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
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 = TEXT_COLOR;
label.shadowColor = [UIColor colorWithWhite:0.9f alpha:1.0f];
label.shadowOffset = CGSizeMake(0.0f, 1.0f);
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
[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 = TEXT_COLOR;
label.shadowColor = [UIColor colorWithWhite:0.9f alpha:1.0f];
label.shadowOffset = CGSizeMake(0.0f, 1.0f);
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
[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:@"blueArrow.png"].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;
}
下面代码用来显示最后的更新时间
- (void)refreshLastUpdatedDate {
if ([_delegate respondsToSelector:@selector(egoRefreshTableHeaderDataSourceLastUpdated:)]) {
NSDate *date = [_delegate egoRefreshTableHeaderDataSourceLastUpdated:self];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setAMSymbol:@"AM"];
[formatter setPMSymbol:@"PM"];
[formatter setDateFormat:@"MM/dd/yyyy hh:mm:a"];
_lastUpdatedLabel.text = [NSString stringWithFormat:@"Last Updated: %@", [formatter stringFromDate:date]];
[[NSUserDefaults standardUserDefaults] setObject:_lastUpdatedLabel.text forKey:@"EGORefreshTableView_LastRefresh"];
[[NSUserDefaults standardUserDefaults] synchronize];
[formatter release];
} else {
_lastUpdatedLabel.text = nil;
}
}
然后是设置当前视图的状态;首先在.h文件中定义一个枚举
typedef enum{
EGOOPullRefreshPulling = 0,
EGOOPullRefreshNormal,
EGOOPullRefreshLoading,
} EGOPullRefreshState;
并且定义个状态属性——state并给设置一个set方法,初始化时_state=EGOOPullRefreshNormal。当_state的值变换时,视图中各个控件也相应地变换,- (void)setState:(EGOPullRefreshState)aState{
switch (aState) {
case EGOOPullRefreshPulling:
_statusLabel.text = NSLocalizedString(@"加载新微博...", @"Release to refresh status");
[CATransaction begin];
[CATransaction setAnimationDuration:4];
// _arrowImage.transform = CATransform3DMakeRotation((M_PI / 180.0) * 180.0f, 0.0f, 0.0f, 1.0f);
_arrowImage.transform = CATransform3DMakeRotation(M_PI ,1.0f, 0.0f, 0.0f);
[CATransaction commit];
break;
case EGOOPullRefreshNormal:
if (_state == EGOOPullRefreshPulling) {
[CATransaction begin];
[CATransaction setAnimationDuration:FLIP_ANIMATION_DURATION];
_arrowImage.transform = CATransform3DIdentity;
[CATransaction commit];
}
_statusLabel.text = NSLocalizedString(@"下拉加载...", @"Pull down to refresh status");
[_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 Status");
[_activityView startAnimating];
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
_arrowImage.hidden = YES;
[CATransaction commit];
break;
default:
break;
}
_state = aState;
}
然后在.h中定义一个一个判断是佛正在加载的属性_loading 和3个方法
- (void)egoRefreshScrollViewDidScroll:(UIScrollView *)scrollView;
- (void)egoRefreshScrollViewDidEndDragging:(UIScrollView *)scrollView;
- (void)egoRefreshScrollViewDataSourceDidFinishedLoading:(UIScrollView *)scrollView;
当用户刚开始滑动调用第一个方法,将_loading设置为no;此时处于视图为“下拉加载”状态;此时用户滑动位移值contentOffset.y大于-65-offsetY小于0;当contentOffset.y<-65-offsetY时_state由EGOOPullRefreshNormal变为EGOOPullRefreshPulling;此时视图为“加载新微博”;但两种状态只有在_loading为NO时互相转换,因为处于加载时视图是不能变化的;当ontentOffset.y<-65-offsetY并停止拖拽时调用第二个方法,将_statue的值变为EGOOPullRefreshLoading;此时处于加载状态,为了防止在加载时滑动影响加载状态,可以用_loading 约束,当_loading 为YES时以上操作都不能执行;因为加载的完成不能由该类中控制,所以_loading指的设置和获取可以通过
设置代理来实现,因此在.h设置了一个代理。
@protocol EGORefreshTableHeaderDelegate
//- (void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView*)view;
//- (BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView*)view;
@optional
- (NSDate*)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView*)view;
当加载结束后用户调用第三个方法,让_statu变 EGOOPullRefreshNormal,tableview也变为初始状态。这样下拉刷新就实现就完成了。- (void)egoRefreshScrollViewDataSourceDidFinishedLoading:(UIScrollView *)scrollView {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.3];
[scrollView setContentInset:UIEdgeInsetsMake(offsetY, 0.0f, 0.0f, 0.0f)];
[UIView commitAnimations];
[self setState:EGOOPullRefreshNormal];
}