打開開源中國iOS客戶端應用程序第一步就是加載數據,常常咱們在第二次之後打開的時候,咱們界面顯示的是上一次更新的數據,此時咱們想看最新內容就須要去刷新數據加載這些內容,加載須要一個等待過程,如何能讓用戶在等待過程當中不焦急,可以等待這個過程完成,這就須要給用戶一個內心安慰,讓用戶知道該軟件正在很努力很努力的執行本身命令,這就須要咱們爲本身應用程序添加一些特效; git
開源中國iOS客戶端用到了很多特效,這些特效在當前不少應用軟件中都比較流行,基本上這些特效都屬於第三方類庫,本次想說的是下拉刷新特效,EGOTableViewPullRefresh最開始是在Twitter中使用,最後作了開源,而後不少應用添加這個特效,常做爲加載數據時將等待時間做爲一個動畫來過渡; github
下拉刷新類庫EGOTableViewPullRefresh資源文件下載地址: app
https://github.com/enormego/EGOTableViewPullRefresh/tree/ 框架
先這個特效的效果圖 函數

在EGOTableViewPullRefresh資源文件中有兩個文件,.m和.h文件,還有資源圖片,就是下拉刷新箭頭 學習

資源圖片一共4種色,能夠根據喜愛選用不一樣色的箭頭,只需在EGORefreshTableHeaderView.m文件中修改一下。按照大小尺寸又可分兩種,較大尺寸是用於iPad上使用的。 動畫

針對這些第三方類庫,咱們不必去深刻研究它們內部實現機制原理,只要知道怎麼用就能夠。不過,看一看別人實現原理,學學別人的方法仍是很不錯的,瞭解下人家牛人程序是怎麼寫的; atom
EGORefreshTableHeaderView.h spa
- #import <UIKit/UIKit.h>
- #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;
- - (void)egoRefreshScrollViewDidScroll:(UIScrollView *)scrollView;
- - (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
首先是定義了一個枚舉類型EGOPullRefreshState表示當前咱們操做在哪一種狀態下,有下拉狀態、正常狀態、數據加載狀態;
@protocol EGORefreshTableHeaderDelegate;表示聲明有這個協議,該協議裏面聲明瞭一些方法,只要其餘的類遵循了這個協議(也就是遵循了它的規定),就能夠去實現協議裏面方法,協議裏的方法是留給遵循這個協議的類去實現的,也是留給外部實現接口; .net
EGORefreshTableHeaderView成員變量定義兩個label用於提示下拉過程所處狀態,和顯示的刷新時間。定義的CALayer類對象裝載顯示圖片。UIActivityIndicatorView類對象顯示一個等待動畫;
@property(nonatomic,assign)id <EGORefreshTableHeaderDelegate> delegate;聲明一個協議對象;
接着下面的是EGORefreshTableHeaderView類成員函數,用於實現類庫中下拉刷新的效果;
最後定義了4個協議方法,其中最後一個協議方法爲可選實現;
下面是EGORefreshTableHeaderView.m文件,想說的都在註釋裏
- #import "EGORefreshTableHeaderView.h"
-
-
- #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])) {
- // self.view自動適應bounds的寬度
- self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
- // self.view背景色和透明度設置
- 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文本陰影顏色
- 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在view上以某種形式適應
- layer.contentsGravity = kCAGravityResizeAspect;
- layer.contents = (id)[UIImage imageNamed:arrow].CGImage;
-
- // 判斷設備版本,由於一些iOS特性是在最後新增的,要求設備配置高一些,因此作一下判斷
- #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;
-
- }
- //初始化當前視圖的frame
- - (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:)]) {
-
- NSDate *date = [_delegate egoRefreshTableHeaderDataSourceLastUpdated:self];
- // NSDateFormatter實例建立字符串,來表示NSDate和NSCalendarDate對象,已預訂格式化字符串輸出
- [NSDateFormatter setDefaultFormatterBehavior:NSDateFormatterBehaviorDefault];
- NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
- // 設置日期輸出格式
- [dateFormatter setDateStyle:NSDateFormatterShortStyle];
- // 設置時間顯示格式
- [dateFormatter setTimeStyle:NSDateFormatterShortStyle];
-
- // _lastUpdatedLabel.text = [NSString stringWithFormat:@"Last Updated: %@", [dateFormatter stringFromDate:date]];
- _lastUpdatedLabel.text = [NSString stringWithFormat:@"最後更新: %@", [dateFormatter stringFromDate:date]];
- // 存儲_lastUpdatedLabel.text內容,放到字典中
- [[NSUserDefaults standardUserDefaults] setObject:_lastUpdatedLabel.text forKey:@"EGORefreshTableView_LastRefresh"];
- // 將NSUserDefaults存儲數據放到磁盤
- [[NSUserDefaults standardUserDefaults] synchronize];
-
- } else {
-
- _lastUpdatedLabel.text = nil;
-
- }
-
- }
- - (void)setState:(EGOPullRefreshState)aState{
-
- switch (aState) {
- /*觸摸屏幕下拉狀態*/
- case EGOOPullRefreshPulling:
-
- // _statusLabel.text = NSLocalizedString(@"Release to refresh...", @"Release to refresh status");
- _statusLabel.text = @"鬆開便可刷新";
- // 設置下拉刷新過程,箭頭的圖片的一個動畫過程
- [CATransaction begin];
- // 動畫時間
- [CATransaction setAnimationDuration:FLIP_ANIMATION_DURATION];
- // 下拉刷新箭頭一個翻轉過程,(M_PI / 180.0)是角度轉換爲弧度
-
- _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;
- [CATransaction commit];
- }
-
- // _statusLabel.text = NSLocalizedString(@"Pull down to refresh...", @"Pull down to refresh status");
- _statusLabel.text = @"下拉能夠刷新";
- [_activityView stopAnimating];
- [CATransaction begin];
- // 由於下拉刷新完成好就不須要下拉動畫,此時_activityView動畫顯示
- // 顯示事物關閉動畫效果 kCFBooleanTrue關閉 kCFBooleanFalse開啓
- [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 = @"加載中";
- [_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
-
- - (void)egoRefreshScrollViewDidScroll:(UIScrollView *)scrollView {
-
- if (_state == EGOOPullRefreshLoading) {
-
- 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) {
-
- BOOL _loading = NO;
- 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];
- } else if (_state == EGOOPullRefreshNormal && scrollView.contentOffset.y < -65.0f && !_loading) {
- [self setState:EGOOPullRefreshPulling];
- }
- // 設置下拉屬性scrollView框架恢復初始位置
- if (scrollView.contentInset.top != 0) {
- // A UIEdgeInsets struct whose top, left, bottom, and right fields are all set to the value 0.
- scrollView.contentInset = UIEdgeInsetsZero;
- }
-
- }
-
- }
-
- - (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恢復位置大小
- [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
當咱們想使用這個下拉刷新類庫的時候,在使用類裏聲明這個協議<EGORefreshTableHeaderDelegate>,把當前類self交付給下拉刷新庫的協議對象,也就是xx.delegate=self;
怎樣讓其餘類來使用這裏面效果,這時咱們就能夠委託另外一個類來實現協議的方法。
選中一個協議方法,右鍵選擇Jump to Definition就能夠看到哪些類被委託了,怎樣使用了這個類的協議方法:
正在學習過程當中,錯誤之處請指正,歡迎交流,共同窗習;