iOS動畫 — 轉(zhuǎn)場 — TabBar滑動

之前有涉及到 CATransition View 的轉(zhuǎn)場,但實際上可能我們用到 VC 的轉(zhuǎn)場可能更多一些。此時我先從一個需求出發(fā)舉例,就是 ** tabBar 增加滑動屏幕切換 item 的效果**绪杏。

TabBar 滑動
  • 1、動畫交互 —— UIViewControllerAnimatedTransitioning
  • 2纽绍、什么時候需要動畫交互 —— UITabBarControllerDelegate
  • 3蕾久、真正執(zhí)行的地方 —— UITabBarController

1、動畫交互 —— UIViewControllerAnimatedTransitioning

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

typedef NS_ENUM(NSInteger,TabOperationDirection) {
    TabLeftDirection,
    TabRightDirection
};

@interface ScrollTabBarAnimator : NSObject <UIViewControllerAnimatedTransitioning>

@property (nonatomic, assign) TabOperationDirection tabScrollDirection;

@end
#import "ScrollTabBarAnimator.h"

@implementation ScrollTabBarAnimator

//動畫持續(xù)時間
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
    return 0.3;
}

//動畫執(zhí)行效果
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
    
    // 獲取 toView fromView
    UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIView *containerView = [transitionContext containerView];
    if (!toViewController || !fromViewController || !containerView) return;
    
    // 給 toView fromView 設定相應的值
    toViewController.view.transform = CGAffineTransformIdentity;
    fromViewController.view.transform = CGAffineTransformIdentity;
    CGFloat translation = containerView.frame.size.width;

    switch (self.tabScrollDirection) {
        case TabLeftDirection:
            translation = translation;
            break;
        case TabRightDirection:
            translation = -translation;
            break;
        default:
            break;
    }

    [containerView addSubview:toViewController.view];
    toViewController.view.transform = CGAffineTransformMakeTranslation(-translation, 0);
    // 真正的變化
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromViewController.view.transform = CGAffineTransformMakeTranslation(translation, 0);
        toViewController.view.transform = CGAffineTransformIdentity;
    } completion:^(BOOL finished) {
        fromViewController.view.transform = CGAffineTransformIdentity;
        toViewController.view.transform = CGAffineTransformIdentity;
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

@end

2拌夏、什么時候需要動畫交互 —— UITabBarControllerDelegate

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface ScrollTabBarDelegate : NSObject <UITabBarControllerDelegate>

@property (nonatomic, assign) BOOL interactive;
@property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactionController;

@end
#import "ScrollTabBarDelegate.h"
#import "ScrollTabBarAnimator.h"


@interface ScrollTabBarDelegate ()

@property (nonatomic, strong) ScrollTabBarAnimator *tabBarAnimator;

@end

@implementation ScrollTabBarDelegate

- (instancetype)init {
    if (self = [super init]) {
        _interactive = NO;
        _interactionController = [[UIPercentDrivenInteractiveTransition alloc] init];
        _tabBarAnimator = [[ScrollTabBarAnimator alloc] init];

    }
    return self;
}

- (nullable id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController
                               interactionControllerForAnimationController: (id <UIViewControllerAnimatedTransitioning>)animationController {
    return self.interactive ? self.interactionController : nil;
}

- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
                     animationControllerForTransitionFromViewController:(UIViewController *)fromVC
                                                       toViewController:(UIViewController *)toVC {
    
    NSInteger fromIndex = [tabBarController.viewControllers indexOfObject:fromVC];
    NSInteger toIndex = [tabBarController.viewControllers indexOfObject:toVC];
    self.tabBarAnimator.tabScrollDirection = (toIndex < fromIndex) ? TabLeftDirection: TabRightDirection;
    return self.tabBarAnimator;

}

@end

3僧著、真正執(zhí)行的地方 —— UITabBarController

#import "ScrollTabBarController.h"
#import "ScrollTabBarDelegate.h"

@interface ScrollTabBarController ()

@property (nonatomic, strong) UIPanGestureRecognizer *panGesture;
@property (nonatomic, assign) NSInteger subViewControllerCount;
@property (nonatomic, strong) ScrollTabBarDelegate *tabBarDelegate;

@end

@implementation ScrollTabBarController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 正確的給予 count
    self.subViewControllerCount = self.viewControllers ? self.viewControllers.count : 0;
    // 代理
    self.tabBarDelegate = [[ScrollTabBarDelegate alloc] init];
    self.delegate = self.tabBarDelegate;
    // 增加滑動手勢
    self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panHandle:)];
    [self.view addGestureRecognizer:self.panGesture];

}

- (void)panHandle:(UIPanGestureRecognizer *)panGesture {
    // 獲取滑動點
    CGFloat translationX = [panGesture translationInView:self.view].x;
    CGFloat progress = fabs(translationX)/self.view.frame.size.width;
    
    switch (panGesture.state) {
        case UIGestureRecognizerStateBegan:
        {
            self.tabBarDelegate.interactive = YES;
            CGFloat velocityX = [panGesture velocityInView:self.view].x;
            if (velocityX < 0) {
                if (self.selectedIndex < self.subViewControllerCount - 1) {
                    self.selectedIndex += 1;
                }
            }
            else {
                if (self.selectedIndex > 0) {
                    self.selectedIndex -= 1;
                }
            }
        }
            break;
        case UIGestureRecognizerStateChanged:
        {
            [self.tabBarDelegate.interactionController updateInteractiveTransition:progress];
        }
            
            break;
        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateFailed:
        case UIGestureRecognizerStateCancelled:
        {
            if (progress > 0.3) {
                self.tabBarDelegate.interactionController.completionSpeed = 0.99;
                [self.tabBarDelegate.interactionController finishInteractiveTransition];
            }else{
                //轉(zhuǎn)場取消后,UITabBarController 自動恢復了 selectedIndex 的值障簿,不需要我們手動恢復盹愚。
                self.tabBarDelegate.interactionController.completionSpeed = 0.99;
                [self.tabBarDelegate.interactionController cancelInteractiveTransition];
            }
            self.tabBarDelegate.interactive = NO;
        }
            break;
        default:
            break;
    }
}

@end

大致效果就出來,還有點瑕疵站故,大致對自定義轉(zhuǎn)場的感受分為:

  • 第一步知道我們要什么效果皆怕,切換中應該發(fā)生什么?西篓。
//返回動畫的時間
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext;
//切換時的UIView的設置和動畫
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext; 
  • 第二步愈腾,這個效果在什么地方實現(xiàn)。
//UINavigationController 的 delegate 屬性遵守該協(xié)議岂津。
<UINavigationControllerDelegate> 
//UITabBarController 的 delegate 屬性遵守該協(xié)議虱黄。
<UITabBarControllerDelegate> 
//UIViewController 的 transitioningDelegate 屬性遵守該協(xié)議。
<UIViewControllerTransitioningDelegate> 

上述例子用的就是UITabBarControllerDelegate吮成,但實際上我們很多時候用的是UIViewControllerTransitioningDelegate礁鲁。

  • 第三步就是真正的實現(xiàn),是否需要使用自定義的切換效果赁豆。
@protocol UIViewControllerTransitioningDelegate;

@interface UIViewController(UIViewControllerTransitioning)

@property (nullable, nonatomic, weak) id <UIViewControllerTransitioningDelegate> transitioningDelegate NS_AVAILABLE_IOS(7_0);

@end
detailVC.transitioningDelegate = self;
[self.navigationController presentViewController:detailVC animated:YES completion:nil];

至于上面那個例子仅醇,增加手勢滑動,是特殊一點的場景魔种,實際上還是 transitioningDelegate 的另一種展示析二。

總的說來,具體的效果還是得看第一步中的實現(xiàn)节预,目前我還沒有深入叶摄,繼續(xù)學習吧,決定多看看這個經(jīng)典的VCTransitionsLibrary中的例子!

PS : 上述代碼已放在:ScrollTabBar安拟,歡迎提出問題蛤吓。

備注參考:
http://kittenyang.com/uiviewcontrollertransitioning/
https://github.com/seedante/iOS-Note/wiki/ViewController-Transition

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市糠赦,隨后出現(xiàn)的幾起案子会傲,更是在濱河造成了極大的恐慌锅棕,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淌山,死亡現(xiàn)場離奇詭異裸燎,居然都是意外死亡,警方通過查閱死者的電腦和手機泼疑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門德绿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人退渗,你說我怎么就攤上這事移稳。” “怎么了会油?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵个粱,是天一觀的道長。 經(jīng)常有香客問我钞啸,道長,這世上最難降的妖魔是什么喇潘? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任体斩,我火速辦了婚禮,結(jié)果婚禮上颖低,老公的妹妹穿的比我還像新娘絮吵。我一直安慰自己,他們只是感情好忱屑,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布蹬敲。 她就那樣靜靜地躺著,像睡著了一般莺戒。 火紅的嫁衣襯著肌膚如雪伴嗡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天从铲,我揣著相機與錄音瘪校,去河邊找鬼。 笑死名段,一個胖子當著我的面吹牛阱扬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伸辟,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼麻惶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了信夫?” 一聲冷哼從身側(cè)響起窃蹋,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤卡啰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后脐彩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碎乃,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年惠奸,在試婚紗的時候發(fā)現(xiàn)自己被綠了梅誓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡佛南,死狀恐怖梗掰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嗅回,我是刑警寧澤及穗,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站绵载,受9級特大地震影響埂陆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜娃豹,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一焚虱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧懂版,春花似錦鹃栽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蓬抄,卻和暖如春丰嘉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嚷缭。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工供嚎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人峭状。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓克滴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親优床。 傳聞我的和親對象是個殘疾皇子劝赔,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

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