之前有涉及到 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