快速封裝Airbnb風(fēng)格Loadingview

前言

一直在觀察各種App的LoadingView,比較有代表性的是MBProgressVHUD,SVProgressHUD,這兩個(gè)使用得非常廣泛,大到QQ,支付寶,小到各種不知道名的App,長時(shí)間的迭代讓它們的邏輯非常完善,同時(shí)也導(dǎo)致了有些累贅,如果把它們當(dāng)一個(gè)產(chǎn)品來分析,可以看出,它們在不斷地增加需求和使用場景,有沒有一個(gè)非常簡潔的HUD沒有我們不需要的那些多余的邏輯只是負(fù)責(zé)顯示指示和隱藏呢?

好像沒有,所有今天動(dòng)手準(zhǔn)備自己封裝一個(gè)LoadingView,靈感來自Airbnb,用過Airbnb的同學(xué)都知道,它的LoadingView很有風(fēng)格,Airbnb是幾張圖片循環(huán)翻轉(zhuǎn)切換,當(dāng)然我不準(zhǔn)備復(fù)制他們的idea,我準(zhǔn)備做一個(gè)循環(huán)左右上下切換的LoadingView。

廢話不多說,先來看一下,最終效果的原型圖

RFLoadingView

思路

分析一下思路:

  • 四條虛線交叉形成的區(qū)域就是我們能夠看到的圖片
  • 首先準(zhǔn)備兩張圖片,位置 中+上
  • 開始第一段動(dòng)畫,向下切換,位置 變成 中+下
  • 第一段動(dòng)畫結(jié)束,將下面的圖片移動(dòng)到右邊,準(zhǔn)備開始第二段動(dòng)畫
  • 第二動(dòng)畫跟第一段類似,只是方向是從右向左,動(dòng)畫結(jié)束后 位置變成 中+左
  • 將左邊的圖片移動(dòng)到上方位置,完成一個(gè)循環(huán)

為了使動(dòng)畫更流暢不至于生硬,我們使用iOS7推出的帶彈簧效果的API

 
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);
 

API接口

在API接口設(shè)計(jì)上,我希望盡量簡單實(shí)用,封裝了兩個(gè)方法,一個(gè)用來顯示,一個(gè)用來隱藏,可以指定顯示到某個(gè)view,以及指定顯示和隱藏時(shí)是否使用動(dòng)畫。

 
+ (void)showViewAddedTo:(UIView *)view animated:(BOOL)animated;
+ (void)hideViewForView:(UIView *)view animated:(BOOL)animated;

API實(shí)現(xiàn)

根據(jù)剛才的分析,核心的動(dòng)畫實(shí)現(xiàn)已經(jīng)有了思路,現(xiàn)在就是怎么設(shè)計(jì)內(nèi)部代碼實(shí)現(xiàn),為了方便顯示蒙版阻止加載的時(shí)候用戶交互,我把view的背景顏色設(shè)置了一個(gè)淡灰色,view的中間有三個(gè)子view,一個(gè)是位于中間的容器centralView,負(fù)責(zé)顯示我們所看到的區(qū)域,方便實(shí)現(xiàn)圓角和動(dòng)畫效果,里面加入兩個(gè)子view,firstViewsecondView用于顯示動(dòng)畫切換的圖片。

內(nèi)部接口大概這樣


@property (nonatomic, strong) UIView      *centralView;
@property (nonatomic, strong) UIImageView *firstView;
@property (nonatomic, strong) UIImageView *secondView;

為了防止多次添加LoadingView,在每次添加前,我們會(huì)查找該view是否存在,如果不存在,創(chuàng)建一個(gè)新的對象,如果存在直接跳過添加操作。反向遍歷,快速查找。


+ (RFLoadingView *)loadingViewForView:(UIView *)view
{
    NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
    for (UIView *subview in subviewsEnum) {
        if ([subview isKindOfClass:self]) {
            return (RFLoadingView *)subview;
        }
    }
    
    return nil;
}

動(dòng)畫核心實(shí)現(xiàn),兩端動(dòng)畫,封裝成兩個(gè)方法

  • - (void)animatedImageFromTopToBottom
  • - (void)animatedImageFromTopToBottom

具體實(shí)現(xiàn)如下


- (void)animatedImageFromTopToBottom
{
    [UIView animateWithDuration:0.3 delay:0.5 usingSpringWithDamping:0.7 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        _firstView.centerY  += kCenterViewSize;
        _secondView.centerY += kCenterViewSize;
    } completion:^(BOOL finished) {
        _firstView.centerX += kCenterViewSize;
        _firstView.centerY -= kCenterViewSize;
        
        [self changeFirstImage];
        [self animatedImageFromRightToLeft];
    }];
}

- (void)animatedImageFromRightToLeft
{
    
    [UIView animateWithDuration:0.3 delay:0.5 usingSpringWithDamping:0.7 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        _firstView.centerX  -= kCenterViewSize;
        _secondView.centerX -= kCenterViewSize;
    } completion:^(BOOL finished) {
        _secondView.centerX += kCenterViewSize;
        _secondView.centerY -= kCenterViewSize;
        
        if (self.alpha && self) {
            [self changeSecondImage];
            [self animatedImageFromTopToBottom];
        }
        
    }];


需要非常注意的是,一定要寫上終止動(dòng)畫的條件,不然會(huì)無限循環(huán),影響性能

show和hide

- (void)showAnimated:(BOOL)animated
{
    RFMainThreadAssert();
    
    self.alpha = 1;
    
    [UIView animateWithDuration:animated ? 0.3 : 0 animations:^{
        _centralView.alpha = 1;
    } completion:^(BOOL finished) {
        [self animatedImageFromTopToBottom];
    }];
}

- (void)hideAnimated:(BOOL)animated
{
    RFMainThreadAssert();
    
    [UIView animateWithDuration:animated ? 0.3 : 0 animations:^{
        self.alpha = 0;
    } completion:^(BOOL finished) {
        [self removeFromSuperview];
    }];
}

最終效果

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容