iOS动画 — 转场 — TabBar滑动
2016-08-14 本文已影响1397人
天空中的球
TabBar 滑动之前有涉及到 CATransition View 的转场,但实际上可能我们用到 VC 的转场可能更多一些。此时我先从一个需求出发举例,就是 ** tabBar 增加滑动屏幕切换 item 的效果**。
- 1、动画交互 —— UIViewControllerAnimatedTransitioning
- 2、什么时候需要动画交互 —— UITabBarControllerDelegate
- 3、真正执行的地方 —— 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
//动画持续时间
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.3;
}
//动画执行效果
- (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、真正执行的地方 —— 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{
//转场取消后,UITabBarController 自动恢复了 selectedIndex 的值,不需要我们手动恢复。
self.tabBarDelegate.interactionController.completionSpeed = 0.99;
[self.tabBarDelegate.interactionController cancelInteractiveTransition];
}
self.tabBarDelegate.interactive = NO;
}
break;
default:
break;
}
}
@end
大致效果就出来,还有点瑕疵,大致对自定义转场的感受分为:
- 第一步知道我们要什么效果,切换中应该发生什么?。
//返回动画的时间
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext;
//切换时的UIView的设置和动画
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
- 第二步,这个效果在什么地方实现。
//UINavigationController 的 delegate 属性遵守该协议。
<UINavigationControllerDelegate>
//UITabBarController 的 delegate 属性遵守该协议。
<UITabBarControllerDelegate>
//UIViewController 的 transitioningDelegate 属性遵守该协议。
<UIViewControllerTransitioningDelegate>
上述例子用的就是UITabBarControllerDelegate,但实际上我们很多时候用的是UIViewControllerTransitioningDelegate。
- 第三步就是真正的实现,是否需要使用自定义的切换效果。
@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
的另一种展示。
总的说来,具体的效果还是得看第一步中的实现,目前我还没有深入,继续学习吧,决定多看看这个经典的VCTransitionsLibrary中的例子!
PS : 上述代码已放在:ScrollTabBar,欢迎提出问题。
备注参考:
http://kittenyang.com/uiviewcontrollertransitioning/
https://github.com/seedante/iOS-Note/wiki/ViewController-Transition