There are roughly three types of switching between view controllers
- UITabBarController Switches its subview when its subview controller is selected
- UINavigationController pushes/pops a subview
- A UIViewController presented/ Presented Early
Custom non-interactive transition animation
General part 1, in order to realize the custom transition animations ViewController implementation of corresponding delegate, UITabBarViewController corresponding is UITabBarControllerDelegate, UINavigationController is corresponding UINavigationControllerDelegate. 2, at the beginning of the animation, the delegate calls the corresponding animation controller, which is realized UIViewControllerAnimatedTransitioning protocol classes, this class can be any type. If nil is returned, the default animation is used. 3, animation controller to achieve the corresponding method
- TransitionDuration: animation execution duration
- AnimateTransition: A place to implement custom animations
To implement a simple example in the code comment, change the UITabBarController view switch mode to left/right movement mode Swift version: This is a simple example, so create a Tabbled Application template for the project to implement the AppDelegate
import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // Override point for customization after application launch. (self.window! .rootViewController as! UITabBarController).delegate = self return true } } extension AppDelegate : UITabBarControllerDelegate{ func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { return self }} extension AppDelegate : UIViewControllerAnimatedTransitioning {/ / the execution time of the animation, Func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) - > NSTimeInterval 0.4} {return func animateTransition (transitionContext: UIViewControllerContextTransitioning) { let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! let containerView = transitionContext.containerView()! / / this method is iOS8 before some let the view1. = transitionContext viewForKey (UITransitionContextFromViewKey)! let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)! // let view11 = vc1. View // let view22 = vc2 transitionContext.initialFrameForViewController(vc1) let r2end = transitionContext.finalFrameForViewController(vc2) //which key we are going depends on which vc is which the most general way to express this is in terms of index number let tab = self.window! .rootViewController as! UITabBarController let index1 = tab.viewControllers! .indexOf(vc1) let index2 = tab.viewControllers! .indexOf(vc2) let dir : CGFloat = index1 < index2 ? 1: -1 var r1end = r1start r1end.origin.x -= r1end.width * dir var r2start = r2end r2start.origin.x += r2start.width * dir View2.frame = r2start containerView.addSubView (view2) // Avoid interference from other gesture events UIApplication. SharedApplication (.) beginIgnoringInteractionEvents () / / perform specific animation, Different effects of the code is in the UIView. AnimateWithDuration (0.4, animations: {the view1. Frame = r1end view2. Frame = r2end}) {(_) in / / prevent cancel state transitionContext.com pleteTransition (! transitionContext.transitionWasCancelled()) UIApplication.sharedApplication().endIgnoringInteractionEvents() } } func animationEnded(transitionCompleted: Bool) { if transitionCompleted { print("Completed") }else{ print("NO Completed") } } }Copy the code
To keep the code a little cleaner and to make the animation a little easier to use, we’ll separate it out.
import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // Override point for customization after application launch. (self.window! .rootViewController as! UITabBarController).delegate = self return true }Copy the code
}
extension AppDelegate : UITabBarControllerDelegate{ func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { let animation = CustomTransitionAimation() animation.tab = (self.window! .rootViewController as! UITabBarController) return animation } }Copy the code
The implementation of animation classes
import UIKit class CustomTransitionAimation: NSObject,UIViewControllerAnimatedTransitioning { var tab : UITabBarController? AnimateTransition: func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {return 0.4} func animateTransition(transitionContext: UIViewControllerContextTransitioning) { let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! let containerView = transitionContext.containerView()! / / this method is iOS8 before some let the view1. = transitionContext viewForKey (UITransitionContextFromViewKey)! let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)! // let view11 = vc1. View // let view22 = vc2 transitionContext.initialFrameForViewController(vc1) let r2end = transitionContext.finalFrameForViewController(vc2) //which key we are going depends on which vc is which the most general way to express this is in terms of index number let tab = self.tab! let index1 = tab.viewControllers! .indexOf(vc1) let index2 = tab.viewControllers! .indexOf(vc2) let dir : CGFloat = index1 < index2 ? 1: -1 var r1end = r1start r1end.origin.x -= r1end.width * dir var r2start = r2end r2start.origin.x += r2start.width * dir View2.frame = r2start containerView.addSubView (view2) // Avoid interference from other gesture events UIApplication. SharedApplication (.) beginIgnoringInteractionEvents () / / perform specific animation, Different effects of the code is in the UIView. AnimateWithDuration (0.4, animations: {the view1. Frame = r1end view2. Frame = r2end}) {(_) in / / prevent cancel state transitionContext.com pleteTransition (! transitionContext.transitionWasCancelled()) UIApplication.sharedApplication().endIgnoringInteractionEvents() } } func animationEnded(transitionCompleted: Bool) { if transitionCompleted { print("Completed") }else{ print("NO Completed") } } }Copy the code
Objective – C version
#import "AppDelegate.h" #import "CusomTransitionAnimation.h" @interface AppDelegate ()<UITabBarControllerDelegate> @property(nonatomic,strong) UITabBarController *tabBarController; @property(nonatomic,strong) CusomTransitionAnimation *customAnimation; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. self.tabBarController = (UITabBarController *)self.window.rootViewController; self.tabBarController.delegate = self; return YES; } - (id <UIViewControllerAnimatedTransitioning> )tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { CusomTransitionAnimation *animation = [[CusomTransitionAnimation alloc]init]; animation.tabBarController = self.tabBarController; return animation; } - (CusomTransitionAnimation *)customAnimation { if (! _customAnimation) { _customAnimation = [[CusomTransitionAnimation alloc]init]; _customAnimation.tabBarController = self.tabBarController; } return _customAnimation; } @endCopy the code
Animation class
//.h #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface CusomTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning> @property(nonatomic,strong) UITabBarController *tabBarController; @end //.m #import "CusomTransitionAnimation.h" @implementation CusomTransitionAnimation - (NSTimeInterval) transitionDuration: (nullable id < UIViewControllerContextTransitioning >) transitionContext {return 0.4; } - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { UIView *containerView = [transitionContext containerView]; UIViewController *vc1 = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIViewController *vc2 = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIView *view1 = [transitionContext viewForKey:UITransitionContextFromViewKey]; UIView *view2 = [transitionContext viewForKey:UITransitionContextToViewKey]; CGRect vc1start = [transitionContext initialFrameForViewController:vc1]; CGRect vc2end = [transitionContext finalFrameForViewController:vc2]; NSUInteger index1 = [self.tabBarController.viewControllers indexOfObject:vc1]; NSUInteger index2 = [self.tabBarController.viewControllers indexOfObject:vc2]; int dir = index1 < index2 ? 1:1; CGRect vc1end = vc1start; vc1end.origin.x -= vc1end.size.width * dir; CGRect vc2start = vc2end; vc2start.origin.x += vc2start.size.width * dir; view2.frame = vc2start; [containerView addSubview:view2]; [[UIApplication sharedApplication]beginIgnoringInteractionEvents]; [UIView animateWithDuration:0.4 animations:^{view1.frame = vc1end; view2.frame = vc2end; view2.animateWithDuration :0.4 animations:^{view1.frame = vc1end; view2.frame = vc2end; } completion:^(BOOL finished) { [transitionContext completeTransition:(! transitionContext.transitionWasCancelled)]; [[UIApplication sharedApplication]endIgnoringInteractionEvents]; }]; } - (void)animationEnded:(BOOL)transitionCompleted { if (transitionCompleted) { NSLog(@"Completed"); }else{ NSLog(@"No Completed"); } } @endCopy the code
The UINavigationController implementation is similar, except that the animation class is called differently
ViewController implementation
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. self.navigationController! .delegate = self } @IBAction func pushAction(sender: AnyObject) { let second = SecondViewController() self.navigationController! .pushViewController(second, animated: true) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } extension ViewController : UINavigationControllerDelegate{ func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {/ / operation is used to distinguish a push or pop operation if the operation = =. Push {return CustomPushTransitionAimation ()} else if operation = = .Pop{ return CustomPopTransitionAimation() } return nil } }Copy the code
Push the animation
import UIKit class CustomPushTransitionAimation: NSObject, UIViewControllerAnimatedTransitioning {/ / the execution time of the animation, Func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {return 0.4} func animateTransition(transitionContext: UIViewControllerContextTransitioning) { // let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! // let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! let containerView = transitionContext.containerView()! / / this method is iOS8 before some let the view1. = transitionContext viewForKey (UITransitionContextFromViewKey)! let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)! / / / / before iOS8 way let view11 ='s vc1. View / / let view22 = vc2. View view2. Transform = CGAffineTransformMakeScale (0.1, 0.1) containerView. AddSubview (view2) / / avoid the interference of other gesture events UIApplication sharedApplication () beginIgnoringInteractionEvents () / / perform specific animation, the effect of different code is different in that the UIView. AnimateWithDuration (0.4, animations: {{view2. Transform = CGAffineTransformIdentity}) (_) in / / prevent cancel state transitionContext.com pleteTransition (! transitionContext.transitionWasCancelled()) UIApplication.sharedApplication().endIgnoringInteractionEvents() } } func animationEnded(transitionCompleted: Bool) { if transitionCompleted { print("Completed") }else{ print("NO Completed") } }Copy the code
}
Pop animation
import UIKit class CustomPushTransitionAimation: NSObject, UIViewControllerAnimatedTransitioning {/ / the execution time of the animation, Func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {return 0.4} func animateTransition(transitionContext: UIViewControllerContextTransitioning) { // let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! // let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! let containerView = transitionContext.containerView()! / / this method is iOS8 before some let the view1. = transitionContext viewForKey (UITransitionContextFromViewKey)! let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)! / / / / before iOS8 way let view11 ='s vc1. View / / let view22 = vc2. View view2. Transform = CGAffineTransformMakeScale (0.1, 0.1) containerView. AddSubview (view2) / / avoid the interference of other gesture events UIApplication sharedApplication () beginIgnoringInteractionEvents () / / perform specific animation, the effect of different code is different in that the UIView. AnimateWithDuration (0.4, animations: {{view2. Transform = CGAffineTransformIdentity}) (_) in / / prevent cancel state transitionContext.com pleteTransition (! transitionContext.transitionWasCancelled()) UIApplication.sharedApplication().endIgnoringInteractionEvents() } } func animationEnded(transitionCompleted: Bool) { if transitionCompleted { print("Completed") }else{ print("NO Completed") } } }Copy the code
Interactive custom transition animation
There are two ways to do this
Using percentage drivers
It is easier to use this method, but first implement non-interactive custom animations as described above. Next, implement the callback method
func tabBarController(tabBarController: UITabBarController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
Copy the code
Mainly UIPercentDrivenInteractiveTransition use, essence is frozen animation.
import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var rightEdgr : UIScreenEdgePanGestureRecognizer! var leftEdgr : UIScreenEdgePanGestureRecognizer! var inter : UIPercentDrivenInteractiveTransition! // This class is the key. UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // Override point for customization after application launch. let tab = (self.window! .rootViewController as! UITabBarController) tab.delegate = self let sep = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(pan)) sep.edges = .Right sep.delegate = self tab.view.addGestureRecognizer(sep) self.rightEdgr = sep let sep2 = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(pan)) sep2.edges = .Left sep2.delegate = self tab.view.addGestureRecognizer(sep2) self.leftEdgr = sep2 return true }Copy the code
}
extension AppDelegate : UITabBarControllerDelegate{ func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { // let animation = CustomTransitionAimation() // animation.tab = (self.window! .rootViewController as! UITabBarController) // return animation return self } func tabBarController(tabBarController: UITabBarController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { if self.interacting { return self.inter } return nil } } extension AppDelegate : UIGestureRecognizerDelegate{ func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool { let tab = self.window! .rootViewController as! UITabBarController var result = false if gestureRecognizer == self.rightEdgr { result = (tab.selectedIndex < tab.viewControllers! .count - 1) } else{ result = tab.selectedIndex > 0 } return result } func pan(g : UIScreenEdgePanGestureRecognizer) - > Void {/ / key need to handle the let v = g.v iew! let tab = self.window! .rootViewController as! UITabBarController let delta = g.translationInView(v) let percent = fabs(delta.x/v.bounds.width) switch g.state { case .Began: self.inter = UIPercentDrivenInteractiveTransition() self.interacting = true if g == self.rightEdgr { tab.selectedIndex = tab.selectedIndex + 1 }else{ tab.selectedIndex = tab.selectedIndex - 1 } print("selectedIndex = \(tab.selectedIndex)") case .Changed: self.inter.updateInteractiveTransition(percent) case .Ended: If percent > {0.5 self. Intel. FinishInteractiveTransition () / / call the method, Will quickly before the end of the set of operations} else {self. Intel. CancelInteractiveTransition () / / call the method, will recover} the self. The interacting = false case. The Cancelled: self.inter.cancelInteractiveTransition() self.interacting = false default : break } } } extension AppDelegate : UIViewControllerAnimatedTransitioning {/ / the execution time of the animation, Func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) - > NSTimeInterval 0.4} {return func animateTransition (transitionContext: UIViewControllerContextTransitioning) { let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! let containerView = transitionContext.containerView()! / / this method is iOS8 before some let the view1. = transitionContext viewForKey (UITransitionContextFromViewKey)! let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)! // let view11 = vc1. View // let view22 = vc2 transitionContext.initialFrameForViewController(vc1) let r2end = transitionContext.finalFrameForViewController(vc2) //which key we are going depends on which vc is which the most general way to express this is in terms of index number let tab = self.window! .rootViewController as! UITabBarController let index1 = tab.viewControllers! .indexOf(vc1) let index2 = tab.viewControllers! .indexOf(vc2) let dir : CGFloat = index1 < index2 ? 1: -1 var r1end = r1start r1end.origin.x -= r1end.width * dir var r2start = r2end r2start.origin.x += r2start.width * dir View2.frame = r2start containerView.addSubView (view2) /* // Avoid interference from other gesture events UIApplication. SharedApplication (.) beginIgnoringInteractionEvents () / / perform specific animation, Different effects of the code is in the UIView. AnimateWithDuration (0.4, animations: {the view1. Frame = r1end view2. Frame = r2end}) {(_) in / / prevent cancel state transitionContext.com pleteTransition (! transitionContext.transitionWasCancelled()) UIApplication.sharedApplication().endIgnoringInteractionEvents() } */ let opts : UIViewAnimationOptions = self.interacting ? .CurveLinear : [] if ! The self. The interacting {UIApplication. SharedApplication () beginIgnoringInteractionEvents ()} UIView. AnimateWithDuration (0.4, delay: 0, options: opts, animations: { view1.frame = r1end view2.frame = r2end }) { (_) in let canceld = transitionContext.transitionWasCancelled() transitionContext.completeTransition(! canceld) if UIApplication.sharedApplication().isIgnoringInteractionEvents(){ UIApplication.sharedApplication().endIgnoringInteractionEvents() } } } func animationEnded(transitionCompleted: Bool) { if transitionCompleted { print("Completed") }else{ print("NO Completed") } let tab = self.window! .rootViewController as! UITabBarController print(tab.selectedIndex) } }Copy the code
Do not use percentage drivers
This may seem a little more complicated, but it’s more customizable because you can do things like fromView and toView directly.
The animateTransition method no longer needs to be implemented specifically, but it still needs to exist because its protocol must implement the method.
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
}
Copy the code
Put the code in implementing UIViewControllerInteractiveTransitioning class to implement the agreement. On the way
func startInteractiveTransition(transitionContext: UIViewControllerContextTransitioning){
}
Copy the code
Medium will do.
This time use OC implementation, all familiar with:
#import "AppDelegate.h"
@interface AppDelegate ()<UIViewControllerAnimatedTransitioning,UIViewControllerInteractiveTransitioning,UITabBarControllerDelegate,UIGestureRecognizerDelegate>
@property(nonatomic,strong) id<UIViewControllerContextTransitioning>transitionContext;
@property(nonatomic,strong) UIScreenEdgePanGestureRecognizer *leftEdgr;
@property(nonatomic,strong) UIScreenEdgePanGestureRecognizer *rightEdgr;
@property(nonatomic,assign) BOOL interacting;
@property(nonatomic,assign) CGRect r1end;
@property(nonatomic,assign) CGRect r2start;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UITabBarController *tab = (UITabBarController *)self.window.rootViewController;
tab.delegate = self;
self.leftEdgr = ({
UIScreenEdgePanGestureRecognizer *pan = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
pan.edges = UIRectEdgeLeft;
pan.delegate = self;
[tab.view addGestureRecognizer:pan];
pan;
});
self.rightEdgr = ({
UIScreenEdgePanGestureRecognizer *pan = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
pan.edges = UIRectEdgeRight;
pan.delegate = self;
[tab.view addGestureRecognizer:pan];
pan;
});
self.r1end = CGRectZero;
self.r2start = CGRectZero;
return YES;
}
#pragma mark - Actions
- (void)pan:(UIScreenEdgePanGestureRecognizer *)ges
{
UIView *gesView = ges.view;
UITabBarController *tab = (UITabBarController *)self.window.rootViewController;
CGPoint delta = [ges translationInView:gesView];
CGFloat percent = delta.x/gesView.bounds.size.width;
UIViewController *fromVC,*toVC;
UIView *fromView,*toView;
CGRect fromRectStart,toRectEnd;
if (self.transitionContext) {
fromVC = [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
toVC = [self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
fromView = [self.transitionContext viewForKey:UITransitionContextFromViewKey];
toView = [self.transitionContext viewForKey:UITransitionContextToViewKey];
fromRectStart = [self.transitionContext initialFrameForViewController:fromVC];
toRectEnd = [self.transitionContext finalFrameForViewController:toVC];
}
switch (ges.state) {
case UIGestureRecognizerStateBegan: {
self.interacting = YES;
if (ges == self.leftEdgr) {
tab.selectedIndex = tab.selectedIndex - 1;
}else{
tab.selectedIndex = tab.selectedIndex + 1;
}
break;
}
case UIGestureRecognizerStateChanged:{
//可以在这里对fromView 和 toView 进行直接操作,所以,可定制程度高
fromRectStart.origin.x += (self.r1end.origin.x - fromRectStart.origin.x) * percent;
fromView.frame = fromRectStart;
NSLog(@"fromRect : %@",NSStringFromCGRect(fromRectStart));
CGRect toRectStart = self.r2start;
toRectStart.origin.x += (toRectStart.origin.x - self.r2start.origin.x) * percent;
toView.frame = toRectStart;
NSLog(@"toRect : %@",NSStringFromCGRect(toRectStart));
//在UITabBarController的切换中可能看不出来这句话有什么用处,但是在UINavigationController中这个就很有用了,可以自己处理NavigationBar的效果等。
[self.transitionContext updateInteractiveTransition:percent];
break;
}
case UIGestureRecognizerStateEnded: {
if (percent > 0.5) {
//达到要求,设置成最终状态
[UIView animateWithDuration:0.2 animations:^{
fromView.frame = self.r1end;
toView.frame = toRectEnd;
} completion:^(BOOL finished) {
[self.transitionContext finishInteractiveTransition];
[self.transitionContext completeTransition:YES];
}];
}
else{
//恢复原状
[UIView animateWithDuration:0.2 animations:^{
fromView.frame = fromRectStart;
toView.frame = self.r2start;
} completion:^(BOOL finished) {
[self.transitionContext cancelInteractiveTransition];
[self.transitionContext completeTransition:NO];
}];
}
self.interacting = NO;
self.transitionContext = nil;
break;
}
case UIGestureRecognizerStateCancelled: {
//恢复原状
fromView.frame = fromRectStart;
toView.frame = self.r2start;
[self.transitionContext finishInteractiveTransition];
[self.transitionContext completeTransition:NO];
self.interacting = NO;
self.transitionContext = nil;
break;
}
default:
break;
}}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
UITabBarController *tab = (UITabBarController *)self.window.rootViewController;
BOOL result = NO;
if (gestureRecognizer == self.leftEdgr) {
result = tab.selectedIndex > 0;
}else{
result = tab.selectedIndex < tab.viewControllers.count - 1;
}
return result;
}
#pragma mark - UITabBarControllerDelegate
- (nullable id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController
interactionControllerForAnimationController: (id <UIViewControllerAnimatedTransitioning>)animationController
{
return self.interacting ? self : nil;
}
- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
return self.interacting ? self : nil;
}
#pragma mark - UIViewControllerAnimatedTransitioning
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.4;
}
//这个要实现,但是要保持为空
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
}
#pragma mark - UIViewControllerInteractiveTransitioning
//这个其实就是要实现 animateTransition: 原来实现的功能
- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
self.transitionContext = transitionContext;
//即最终要达到的状态
UIView *containerView = [transitionContext containerView];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
CGRect fromRectStart = [transitionContext initialFrameForViewController:fromVC];
CGRect toRectEnd = [transitionContext finalFrameForViewController:toVC];
UITabBarController *tab = (UITabBarController *)self.window.rootViewController;
NSUInteger fromIndex = [tab.viewControllers indexOfObject:fromVC];
NSUInteger toIndex = [tab.viewControllers indexOfObject:toVC];
int dir = fromIndex < toIndex ? 1 : -1;
CGRect fromRectEnd = fromRectStart;
fromRectEnd.origin.x -= fromRectEnd.size.width * dir;
CGRect toRectStart = toRectEnd;
toRectStart.origin.x += toRectStart.size.width * dir;
toView.frame = toRectStart;
[containerView addSubview:toView];
self.r1end = fromRectEnd;
self.r2start = toRectStart;
}
@end
Copy the code
Custom Chicago ViewController transition animation
There are two main ways to implement this
- Presentation Controller is not used
- Use the presentation controller
Presentation Controller is not used
Here’s the caveat
- The position of the protocol transitioningDelegate declaration
- Distinguish between present and dismiss
- Only if modalPresentationStyle * is not.fullscreen * can you use the following method to determine present or dismiss
In the code
import UIKit class ViewController2: UIViewController { override init(nibName nibNameOrNil: String? , bundle nibBundleOrNil: NSBundle?) { super.init(nibName: nibNameOrNil, bundle: NibBundleOrNil) / / this can no longer perform in viewDidLoad, it's too late to the self. The transitioningDelegate = self} required init? (coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.transitioningDelegate = self } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.view.backgroundColor = UIColor.blueColor() } override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { self.presentingViewController! .dismissViewControllerAnimated(true, completion: nil) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } extension ViewController2 : UIViewControllerTransitioningDelegate{ func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return self } func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return self } } extension ViewController2 : UIViewControllerAnimatedTransitioning{ func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {return 0.4} func animateTransition(transitionContext: UIViewControllerContextTransitioning) { let containerView = transitionContext.containerView()! //For a presentation that is not .FullScreen,the unused view is nil. let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey) let toView = transitionContext.viewForKey(UITransitionContextToViewKey) //distinguish the cases //dismiss if fromView ! = nil {UIView. AnimateWithDuration (0.4, animations: {fromView! FromView. Transform = CGAffineTransformMakeScale (0.1, 0.1). .alpha = 0 }, completion: { (_) in transitionContext.completeTransition(! transitionContext.transitionWasCancelled()) }) } //presenting else if toView ! = nil{ let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! let toRectEnd = transitionContext.finalFrameForViewController(toVC) toView! .frame = CGRectInset(toRectEnd, 40, 40) toView! ToView. Transform = CGAffineTransformMakeScale (0.1, 0.1). .alpha = 0 containerView.addSubview(toView!) //also can modify the containerView // containerView.backgroundColor = UIColor.greenColor() UIView. AnimateWithDuration (0.4, animations: {toView! .transform = CGAffineTransformIdentity toView! . Alpha = 1.0}) {(_) in transitionContext.com pleteTransition (! transitionContext.transitionWasCancelled()) } } } }Copy the code
In the presenting View of the.FullScreen type, it takes the presenting View and adds it to containerView, while in the presenting View of the.FullScreen type, containerView comes before the presenting View, You can’t animate the containerView, and it won’t remove it.
Use the Presentation Controller
A few things to understand before we do that
- The Animation Controller is responsible for the Animation effect, i.e. how does the Chicago View move to its final destination
- The Presentation Controller determines the final Chicago View location and adds views to the containerView, such as translucent effects. For example, if you just implement the UIPresentationController without doing the above work, the default animation will pop up.
Next, implementation: There are several things that need to be done to achieve the above effects
-
When you set the transitioningDelegate property of the Presented viewController, you also need to set its modalPresentationStyle to. Custom, this is required. (Note the code location)
// NB if we want to modify the _animation_, we need to set the transitioningDelegate self.transitioningDelegate = self // if we want to modify the _presentation_, we need to set the style to custom // customize presentation only on iPhone // how will we find out which it is? we have no traitCollection yet... // I know, let's ask the window if UIApplication.sharedApplication().keyWindow! .traitCollection.userInterfaceIdiom == .Phone { self.modalPresentationStyle = .Custom }Copy the code
-
I’m implementing another method
@ the available (iOS 8.0. *) optional public func presentationControllerForPresentedViewController (presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController?Copy the code
The next step is to override its methods in a subclass of UIPresentationController to achieve the desired effect
Method description:
// Position of the presented view in the container view by the end of the presentation transition.
// (Default: container view bounds)
public func frameOfPresentedViewInContainerView() -> CGRect
Copy the code
Return to the final Chicago View location
Use the following methods to add or remove additional views to containerView
presentationTransitionWillBegin
presentationTransitionDidEnd
dismissalTransitionWillBegin
dismissalTransitionDidEnd
Copy the code
Use the following method to lay out the additional views you add
containerViewWillLayoutSubviews
containerViewDidLayoutSubviews
Copy the code
methods
shouldPresentInFullscreen
Copy the code
The default returns true. If false, the presentation is changed to.currentContext
Those are the main ones. End.
Use of Transition coordinators
Focus on the use of a few methods
-
AnimateAlongsideTransition: completion: can be used to have on the view of child views to add animation effects
- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (self.transitionCoordinator ! = nil) { [self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { NSLog(@"Transition Animation"); self.animationView.center = self.view.center; } completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { NSLog(@"Transition completion"); }]; } else{ NSLog(@"Transition is NULL"); }}Copy the code
-
notifyWhenInteractionEndsUsingBlock:
Check whether the gesture is successful when the monitoring view returns. For example in UINavigationController when you swipe back to the left, you need to do that in the previous ViewController.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (self.transitionCoordinator != nil) {
[self.transitionCoordinator notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
if ([context isCancelled]) {
NSLog(@"1 Interaction Cancelled");
return ;
}
NSLog(@"1 Interaction Ends");
}];
}
else{
NSLog(@"1 Transition is NULL");
}
}Copy the code