歡迎光臨
每天分享高質量文章

轉場動畫-仿AppStore跳轉及抖音評論

作者:雨兮林月

鏈接:https://www.jianshu.com/p/c3742d607d43

有錢的捧個錢場,沒錢的捧個人場,看一看瞧一瞧嘞。

 

仿AppStore跳轉

 

仿抖音評論

demo下載地址:

demo-0
demo-1

demo-0為簡化版,方便大家理解。demo-1為優化版,功能代碼都比demo-0多一些,本文主要以demo-1進行講解。

前言

在寫這個demo之前我以為轉場動畫就像女神一樣,離我很遠,日常的專案中根本接觸不到,畢竟系統自帶的就好用了,身輕體柔易推倒。但好的動畫效果就像電影彩蛋一樣,不經意間給用戶一個驚喜,這對App的拉新傳播都很有幫助。而且程式員在寫出後,不僅可以在測試女神面前秀一把操作,還可以讓自己的老闆在投資人面前標榜自己的團隊質量,拉到更多的資金,從而彎道超車讓自己升職加薪,走上人生巔峰。

 

國內在交互這方面做的比較好的公司有很多,比如騰訊和位元組跳動。因為我這個demo主要是寫AppStore跳轉和抖音評論,所以就研究了下【AppStore】、【抖音】、【QQ音樂】這三個產品。

 

接下來我言簡意賅,就此demo的實現過程,過程中進行產品相互的對比,對比產生問題進行一番描述。

1、轉場動畫基本概念

轉場動畫主要是由*轉場動畫*,*跳轉協議*,*手勢交互*三部分組成。

 

轉場動畫是對動畫效果的代碼描述,且遵守UIViewControllerAnimatedTransitioning協議。
跳轉協議是在push,press等協議的相關方法里,傳回動畫物件。
手勢交互就是用手勢來控制動畫的進度,一般都是建立UIPercentDrivenInteractiveTransition的子類。

我這裡就轉場動畫的基本概念不進行過多的描述,網上相關的資料非常多。

2、AppStore效果

AppStore首頁的動畫主頁分為這幾個部分。

 

2.1 視圖部分

長按,視圖縮小,鬆開後,視圖鋪開進入下個界面且有輕微彈簧效果。

點擊,視圖縮小,鬆開後,視圖鋪開進入下個界面且有輕微彈簧效果。

長按後滑動,視圖先縮小,然後恢複原狀。

 

我這裡直接用UIButton處理這些手勢,touchesBegan處理視圖縮小,touchesEnded處理點擊回呼。所以在這裡加了個bool屬性endTouchesBegan用來判斷視圖是否已經縮小。如果縮小,直接回呼,沒有則先進行縮小載回呼。

 

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    self.endTouchesBegan =NO;
    [UIView animateWithDuration:0.2 animations:^{
        self.btn.transform = CGAffineTransformMakeScale(0.97,0.97);
    } completion:^(BOOL finished){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(0.2 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
            self.endTouchesBegan =YES;
        });
    }];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    if(self.endTouchesBegan){
        if(self.block){
            self.block();
        }
    }else{
        [UIView animateWithDuration:0.2 animations:^{
            self.btn.transform = CGAffineTransformMakeScale(0.97,0.97);
        } completion:^(BOOL finished){
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(0.2 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
                if(self.block){
                    self.block();
                }
            });
        }];
    }
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
    [UIView animateWithDuration:0.2 animations:^{
        self.btn.transform = CGAffineTransformIdentity;
    }];
}

 

2.2 statusBar部分

 

AppStore動畫第一個界面的statusBar為顯示,第二個界面隱藏,第三個界面恢復顯示。我們用bool屬性hideStatus判斷顯示隱藏。

 

第一個界面,預設self.hideStatus =NO,進行顯示。當點擊圖片時,呼叫strongSelf.hideStatus =YES進行隱藏;這樣做的目的是,當由第二個界面pop回來時,statusBar先是隱藏的,然後走下麵這個方法,進行statusBar動畫顯示。

 

-(void)viewDidAppear:(BOOL)animated{

    [super viewDidAppear:animated];
    self.hideStatus =NO;

    [UIView animateWithDuration:0.5 animations:^{
        [self setNeedsStatusBarAppearanceUpdate];
    }];
}

 

同理,第二個界面也是這樣處理,但是第二個界面不知道是push進去還是pop進去的,所以增加了push屬性。

 

2.3 tabBar部分

 

tabBar動畫開始想用hidesBottomBarWhenPushed進行隱藏,但是與AppStore轉場動畫不太搭,所以就仿照AppStore的tabBar的動畫在UINavigationControllerDelegate協議方法裡面進行了處理。

 

//消失
if(2 == navigationController.viewControllers.count){

       CGRect tabRect = navigationController.tabBarController.tabBar.frame;
        navigationController.tabBarController.tabBar.frame = CGRectMake(tabRect.origin.x,TLDeviceHeight -tabRect.size.height,tabRect.size.width,tabRect.size.height);

        [UIView animateWithDuration:0.5 animations:^{

            navigationController.tabBarController.tabBar.frame = CGRectMake(tabRect.origin.x,TLDeviceHeight +tabRect.size.height,tabRect.size.width,tabRect.size.height);

        } completion:^(BOOL finished){

            navigationController.tabBarController.tabBar.hidden =YES;
        }];
}
//出現
if(1 == navigationController.viewControllers.count){

        if(navigationController.tabBarController.tabBar.hidden){

           CGRect tabRect = navigationController.tabBarController.tabBar.frame;
            navigationController.tabBarController.tabBar.frame = CGRectMake(tabRect.origin.x,TLDeviceHeight +tabRect.size.height,tabRect.size.width,tabRect.size.height);
            navigationController.tabBarController.tabBar.hidden =NO;

            [UIView animateWithDuration:0.5 animations:^{

                navigationController.tabBarController.tabBar.frame = CGRectMake(tabRect.origin.x,TLDeviceHeight -tabRect.size.height,tabRect.size.width,tabRect.size.height);

            }];
        }
}

 

2.4 pop手勢

 

AppStore轉場pop手勢的上下滑動跟抖音評論的效果非常類似,但與之對比,AppStore頁面還增加了左滑pop手勢。

 

我開始是想用蘋果自帶邊緣手勢UIScreenEdgePanGestureRecognizer來進行處理,但發現這樣只能解決橫向側滑pop,無法解決豎向滑動pop的問題。索性就自己寫了一套手勢,橫向豎向都能支持。橫向的滑動還支持全屏,半屏等距離屬性的設置,寫了全域性的宏TLPanEdgeInside來控制。

 

手勢處理自認為比AppStore與抖音評論的效果還好。因為無論AppStore還是抖音評論,只能朝上或朝下其中一個方向改變,要麼改變UIScrollView的偏移量,要麼改變控制器pop的進度。而我封裝的這套可以上下自由改變,並且可以監測開始的手勢,由上下滑轉左右滑,還是按照上下為基礎。

點擊查看效果

 

2.5 後續問題

 

2.5.1 防止重覆點擊

 

因為push轉場時間為0.8秒,我在第一個控制器加入了以下userEnabled屬性用來防止重覆點擊問題。詳情見demo的代碼。

 

2.5.2 分類方法實現

 

AppStore轉場主要涉及三個方法的重寫:

 

-(NSArray*_Nonnull)tl_transitionUIViewFrameViews;

-(NSString *_Nonnull)tl_transitionUIViewImage;

-(void)setContainScrollView:(UIScrollView *)scrollView isPush:(BOOL)isPush;

 

第一個方法是涉及前一個視圖和後一個視圖裡面動畫控制元件的回呼。

第二個主要就是圖片資源的回呼。

第三個就是為了防止手勢衝突,將所需要規避衝突的UIScrollView視圖傳入進去。

 

2.5.3 導航欄出現隱藏

 

導航欄出現隱藏的判斷是在UINavigationControllerDelegate協議裡面判斷的,但是考慮到專案中有些並不是所有的頁面都需要轉場動畫,所以UINavigationControllerDelegate協議在兩個地方進行了重寫。並且還在UIViewController的分類中重寫的viewWillAppear中進行了判斷,方便常規push和轉場push的自由切換。

 

- (void)swizzViewWillAppear:(BOOL)animated{

    if (TLAnimationUIViewFrame == self.animationType || TLAnimationWindowScale == self.animationType || TLAnimationAppStore == self.animationType) {

        self.navigationController.interactivePopGestureRecognizer.delegate = [TLPushTransitionDelegate shareInstance];
        self.navigationController.delegate = [TLPushTransitionDelegate shareInstance];
        [TLPushTransitionDelegate shareInstance].popController = self;

    }else{
    }

    [self swizzViewWillAppear:animated];
}

3、仿抖音評論

3.1 抖音評論

 

抖音評論手勢的處理和AppStore的一模一樣。只不過AppStore是push,抖音評論是press。

這裡有個鏈接是跟抖音的一模一樣,DouYinComment。這個demo是基於視圖層級彈窗,而我寫的是彈出控制器。

 

同時為了避免快速輕掃產生的閃動,我手勢結束事件加了一個判斷,當速度過快,輕掃的距離過短的時候,直接進行pop或者dismiss。


因為在手機錄製不能檢測到手勢光標,所以就用
DouYinComment代替抖音在模擬器進行錄製。大家可以看到,抖音評論當手指滑出彈框外面的時候,它是以彈框整體偏移量為基礎滾動,而我是按照裡面的UIScrollView偏移量為基礎,個人認為第二種效果更好。

 

if((TLPanDirectionEdgeLeft ==self.startDirection || TLPanDirectionEdgeRight ==self.startDirection)&&[gesture velocityInView:gesture.view].x>= 250){

    //左右輕掃,快速傳回
        [self finishInteractiveTransition];
        [self.disMissController dismissViewControllerAnimated:YES completion:nil];

    }else if((TLPanDirectionEdgeUp ==self.startDirection || TLPanDirectionEdgeDown ==self.startDirection)&&self.scrollView.contentOffset.y <= 0 && TLPanDirectionEdgeUp == directionType &&[gesture velocityInView:gesture.view].y>= 250){

    //上下輕掃,快速傳回
        [self finishInteractiveTransition];
        [self.disMissController dismissViewControllerAnimated:YES completion:nil];

    }else{

    //正常傳回
        if(percentComplete > 0.3f){
            [self finishInteractiveTransition];
        }else{
            [self cancelInteractiveTransition];
        }
}

DouYinComment

 

抖音評論

 

輕掃手勢

 

3.2 QQ音樂

 

在研究轉場動畫時候,註意到QQ音樂有個界面存在一個小問題,就是使用加藤鷹般的手速上下滑動的時候,此界面和頂部空隙會越來越大。應該是手勢和界面偏移量之間處理的問題。

點擊查看效果

4、其他兩種效果

4.1 視圖位移

 

視圖位移

這種效果挺好看的,處理起來也非常簡單。只需把相應的控制元件傳入轉場動畫裡面就行。而且圖片瀏覽功能也可以這樣進行封裝。

 

4.2 視圖縮小

 

視圖縮小

這個也很簡單,不過一般用於沒有導航欄的界面,不然看起來會比較醜。

如果大家有什麼疑問或者建議,歡迎評論。

已同步到看一看
赞(0)

分享創造快樂