如何來(lái)實(shí)現(xiàn)一個(gè)自定義的刷新控件

如何來(lái)實(shí)現(xiàn)一個(gè)自定義刷新控件

來(lái)源于raywenderlich

  • 前言
    在滑動(dòng)視圖上添加下拉刷新是很常見(jiàn)的,現(xiàn)在大多數(shù)人都是使用MJRefresh,今天我簡(jiǎn)單講下如果你要自己寫(xiě)個(gè)刷新控件怎么寫(xiě)秸苗。先看看效果:

    自定義刷新控件.gif

  • 首先自己創(chuàng)建一個(gè)繼承UIView的控件
    在自定義一個(gè)控件的時(shí)候我們首先考慮的是他要實(shí)現(xiàn)的方法伪煤,以及屬性损离,然后再是這些方法的實(shí)現(xiàn)哼审。

  • 屬性有4個(gè)
/** 代理*/
@property(nonatomic,weak)id<AIRefreshViewDelegate> delegate;
/** 填加的滑動(dòng)視圖*/
@property(nonatomic,strong)UIScrollView *scrollView;
/** 是否正在被刷新*/
@property(nonatomic, assign,getter=isRefreshing)BOOL refreshing;
/** 進(jìn)度*/
@property(nonatomic, assign)CGFloat progress;
  • 方法我們需要實(shí)現(xiàn)5個(gè)
/**
 初始化方法

 @param frame 初始frame
 @param scrollView 所要添加的滑動(dòng)視圖
 @return 實(shí)體
 */
- (instancetype)initWithFrame:(CGRect)frame scrollView:(UIScrollView*)scrollView
;

/**
 結(jié)束刷新
 */
- (void)endRefreshing;

/**
 開(kāi)始刷新
 */
- (void)beginRefreshing;

還有兩個(gè)是處理Scrollview的拖拽手勢(shì)和將要結(jié)束拖拽代理事件(AIRefreshView這里我并沒(méi)有實(shí)現(xiàn)Scrollview的代理,只是寫(xiě)了方法名一樣的函數(shù)显设,希望把外部的UIScrollview的代理在AIRefreshView控件中處理)

  • .m
    .h的方法說(shuō)完了框弛,看看.m的屬性,我們需要一個(gè)圓捕捂、飛機(jī)瑟枫、背景圖(這里飛機(jī)用的layer是想盡量使用輕量級(jí)的控件)
/** 圓CAShapeLayer */
@property(nonatomic,strong)CAShapeLayer *ovalShapeLayer;
/** 飛機(jī)layer*/
@property(nonatomic,strong)CALayer *airplaneLayer;
/** 背景圖*/
@property(nonatomic,weak)UIImageView *bgImageView;

首先是初始化方法初始化方法添加圓、飛機(jī)指攒、背景

- (instancetype)initWithFrame:(CGRect)frame scrollView:(UIScrollView*)scrollView
{
    self = [super initWithFrame:frame];
    if (self) {
        self.scrollView         = scrollView;
        _refreshing = NO;
        _progress   = 0.;
        //add the background Image
        UIImageView *imageView  = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"refresh-view-bg"]];
        imageView.contentMode   = UIViewContentModeScaleAspectFill;
        imageView.clipsToBounds = YES;
        self.bgImageView        = imageView;
        [self addSubview:imageView];
        
        //shapeLayer
        self.ovalShapeLayer    = [CAShapeLayer layer];
        self.ovalShapeLayer.strokeColor      = [UIColor whiteColor].CGColor;
        self.ovalShapeLayer.fillColor        = [UIColor clearColor].CGColor;
        self.ovalShapeLayer.lineWidth        = 4.;
        self.ovalShapeLayer.lineDashPattern  = @[@2,@3];
        
        CGFloat refreshRadius                = frame.size.height/2 *.8;
        
        self.ovalShapeLayer.path             = [UIBezierPath bezierPathWithOvalInRect:
                                           CGRectMake(frame.size.width *.5 - refreshRadius,
                                                      frame.size.height *.5 - refreshRadius,
                                                      2 * refreshRadius,
                                                      2 * refreshRadius)].CGPath;
        
        [self.layer addSublayer:self.ovalShapeLayer];
        
        self.airplaneLayer              = [CALayer layer];
        UIImageView *airplaneImage      = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"airplane"]];
        self.airplaneLayer.contents     = (__bridge id _Nullable)(airplaneImage.image.CGImage);
        self.airplaneLayer.bounds       = CGRectMake(0, 0, airplaneImage.frame.size.width, airplaneImage.frame.size.height);
        
        self.airplaneLayer.position     = CGPointMake(frame.size.width * .5 + frame.size.height *.5 *.8,
                                                      frame.size.height * .5);
        [self.layer addSublayer:self.airplaneLayer];
    }
    return self;
}
  • 接下來(lái)重點(diǎn)就在這兩個(gè)代理事件
    在拖拽的代理函數(shù)中計(jì)算力奋,內(nèi)容頂部的偏移量,以及拖拽的進(jìn)度(因?yàn)槲覀兊膭?dòng)畫(huà)顯示需要這個(gè)進(jìn)度)
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat offsetY = MAX(-(scrollView.contentOffset.y+scrollView.contentInset.top), 0.);
    _progress       = MIN(MAX(offsetY / self.frame.size.height, 0.), 1.);
    if (!self.isRefreshing) {
        [self redrawFromProgress:self.progress];
    }
}
/**
 通過(guò)進(jìn)度畫(huà)圓幽七,和飛機(jī)
 
 @param progress 進(jìn)度
 */
- (void)redrawFromProgress:(CGFloat)progress {
    self.airplaneLayer.opacity    = _progress;
    self.ovalShapeLayer.strokeEnd = _progress;
}

在拖拽結(jié)束的時(shí)候判斷是否需要進(jìn)行刷新動(dòng)作景殷,以及調(diào)用刷新動(dòng)畫(huà)、實(shí)現(xiàn),刷新控件代理猿挚。

-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    if (!self.isRefreshing && self.progress >= 1.) {
        if (self.delegate && [self.delegate respondsToSelector:@selector(refreshViewDidRefresh:)]) {
            [self.delegate refreshViewDidRefresh:self];
            [self beginRefreshing];
        }
    }
}

基本的開(kāi)始結(jié)束方法

- (void)beginRefreshing {
    self.refreshing = YES;
    [UIView animateWithDuration:.3 animations:^{
        UIEdgeInsets newInsets       = self.scrollView.contentInset;
        newInsets.top               += self.frame.size.height;
        self.scrollView.contentInset = newInsets;
    }];
}

- (void)endRefreshing {
    self.refreshing   = NO;
    
    [UIView animateWithDuration:.3 delay:0. options:(UIViewAnimationOptionCurveEaseOut) animations:^{
        UIEdgeInsets newInsets       = self.scrollView.contentInset;
        newInsets.top               -= self.frame.size.height;
        self.scrollView.contentInset = newInsets;
    } completion:^(BOOL finished) {
        
    }];
}
  • 動(dòng)畫(huà)
    上面已經(jīng)實(shí)現(xiàn)了基本的刷新咐旧,但是現(xiàn)在缺少動(dòng)畫(huà)。
    你應(yīng)該將開(kāi)始動(dòng)畫(huà)放在獲取數(shù)據(jù)的時(shí)候绩蜻,代碼添加在beginRefreshing最后
    首先是圓圈
    CABasicAnimation *strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
    strokeStartAnimation.fromValue         = @-.5;
    strokeStartAnimation.toValue           = @1.;
    
    CABasicAnimation *strokeEndAnimation   = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    strokeEndAnimation.fromValue           = @0.;
    strokeEndAnimation.toValue             = @1.;
    
    CAAnimationGroup *strokeAniamtionGroup = [CAAnimationGroup animation];
    strokeAniamtionGroup.duration          = 1.5;
    strokeAniamtionGroup.repeatDuration    = 5.;
    strokeAniamtionGroup.animations        = @[strokeStartAnimation,strokeEndAnimation];
    [self.ovalShapeLayer addAnimation:strokeAniamtionGroup forKey:nil];

這段代碼創(chuàng)建了兩個(gè)動(dòng)畫(huà):第一個(gè)strokeStart從-0.5到1.0這是一種投機(jī)取巧的方式铣墨,當(dāng)動(dòng)畫(huà)在-0.5到0.0的時(shí)間不會(huì)做任何事。因?yàn)檫@些值只是代表不可見(jiàn)部分的形狀办绝。
飛機(jī)動(dòng)畫(huà)

   //飛機(jī)動(dòng)畫(huà)
   CAKeyframeAnimation *flightAnimation   = [CAKeyframeAnimation animationWithKeyPath:@"position"];
   flightAnimation.path                   = self.ovalShapeLayer.path;
   flightAnimation.calculationMode        = kCAAnimationPaced;
   //旋轉(zhuǎn)
   CABasicAnimation    *airplanOrientationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
   airplanOrientationAnimation.fromValue  = @0.;
   airplanOrientationAnimation.toValue    = @(M_PI *2);
   
   CAAnimationGroup *flightAnimationGroup = [CAAnimationGroup animation];
   flightAnimationGroup.duration          = 1.5;
   flightAnimationGroup.repeatDuration    = 5.;
   flightAnimationGroup.animations        = @[flightAnimation,airplanOrientationAnimation];
   [self.airplaneLayer addAnimation:flightAnimationGroup forKey:nil];

飛機(jī)動(dòng)畫(huà)主要使用CAKeyframeAnimation動(dòng)畫(huà)使飛機(jī)按照?qǐng)A的路徑走伊约,設(shè)置calculationMode屬性為kCAAnimationPaced使動(dòng)畫(huà)以一個(gè)恒定的速度而且確保沿著這個(gè)路徑走。當(dāng)然也要設(shè)置他的旋轉(zhuǎn)角度通過(guò)airplanOrientationAnimation動(dòng)畫(huà)

最后的效果應(yīng)該是這樣:

最后效果.png

點(diǎn)擊這里可以查看源碼第25個(gè)cell,喜歡的給個(gè)star
源碼位置.png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末孕蝉,一起剝皮案震驚了整個(gè)濱河市屡律,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌降淮,老刑警劉巖超埋,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異佳鳖,居然都是意外死亡霍殴,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門系吩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)来庭,“玉大人,你說(shuō)我怎么就攤上這事穿挨≡鲁冢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵絮蒿,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我叁鉴,道長(zhǎng)土涝,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任幌墓,我火速辦了婚禮但壮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘常侣。我一直安慰自己蜡饵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布胳施。 她就那樣靜靜地躺著溯祸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上焦辅,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天博杖,我揣著相機(jī)與錄音,去河邊找鬼筷登。 笑死剃根,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的前方。 我是一名探鬼主播狈醉,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼惠险!你這毒婦竟也來(lái)了苗傅?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤莺匠,失蹤者是張志新(化名)和其女友劉穎金吗,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體趣竣,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摇庙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了遥缕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卫袒。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖单匣,靈堂內(nèi)的尸體忽然破棺而出夕凝,到底是詐尸還是另有隱情,我是刑警寧澤户秤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布码秉,位于F島的核電站,受9級(jí)特大地震影響鸡号,放射性物質(zhì)發(fā)生泄漏转砖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一鲸伴、第九天 我趴在偏房一處隱蔽的房頂上張望府蔗。 院中可真熱鬧,春花似錦汞窗、人聲如沸姓赤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)不铆。三九已至蝌焚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狂男,已是汗流浹背综看。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岖食,地道東北人红碑。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像泡垃,于是被迫代替她去往敵國(guó)和親析珊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容