zoukankan      html  css  js  c++  java
  • 下拉刷新原理实现

    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
  • 相关阅读:
    CentOS系统一键部署jdk,maven,tomcat,mysql
    使用sed在源文件上直接替换某一行的内容,只替换第一次找到的那行
    MLPerf 机器学习基准测试实战入门(一)NAVIDA-GNMT
    SpringBoot Controller接收参数的几种常用方式(转)
    使用延时队列DelayQueue
    Oracle、MySql、SQLServer 数据分页查询(转)
    SqlServer收缩日志
    防火墙升级导致产环境服务中止20小时的问题
    NFS相关
    jquery.validate不使用submit提交,而是使用button按钮提交
  • 原文地址:https://www.cnblogs.com/yulang314/p/3721770.html
Copyright © 2011-2022 走看看