http://blog.csdn.net/kqjob/article/details/9891065#comments
在移动应用开发中,无论是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的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#import <QuartzCore/QuartzCore.h>
//下拉状态
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 <EGORefreshTableHeaderDelegate> 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
|
来自CODE的代码片
EGOTableViewPullRefresh.h
EGORefreshTableHeaderView.m的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
#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
|