iPhone: come cambiare scheda con un’animazione?

Sto passando da una scheda UITabBarController.selectedIndex in modo programmatico in un’applicazione basata su una barra delle tabs usando UITabBarController.selectedIndex . Il problema che sto cercando di risolvere è come animare la transizione tra le viste. vale a dire. dalla vista della scheda corrente alla vista della scheda selezionata.

Il primo pensiero è stato quello di utilizzare UITabBarControllerDelegate , ma sembra che questo non venga chiamato quando si passa da una scheda a un altro. Sto considerando l’ UITabBarDelegate.didSelectItem : come ansible hook per impostare un’animazione di transizione.

Qualcuno è riuscito ad animare le transizioni? Se sì, come?

Aggiornamento 04/2016 : Justed voleva aggiornarlo per dire grazie a tutti per tutti i voti. Si prega di notare che questo è stato originariamente scritto nel lontano passato … prima di ARC, prima dei vincoli, prima … un sacco di roba! Quindi, per favore, prendilo in considerazione quando decidi se usare queste tecniche. Potrebbero esserci approcci più moderni. Oh, e se ne trovi uno. Si prega di aggiungere una risposta in modo che tutti possano vedere. Grazie.

Qualche tempo dopo …

Dopo molte ricerche ho trovato due soluzioni funzionanti. Entrambi hanno funzionato e hanno fatto l’animazione tra le tabs.

Soluzione 1: transizione dalla vista (semplice)

Questo è il più semplice e utilizza un metodo di transizione UIView predefinito. Con questa soluzione non abbiamo bisogno di gestire le viste perché il metodo fa il lavoro per noi.

 // Get views. controllerIndex is passed in as the controller we want to go to. UIView * fromView = tabBarController.selectedViewController.view; UIView * toView = [[tabBarController.viewControllers objectAtIndex:controllerIndex] view]; // Transition using a page curl. [UIView transitionFromView:fromView toView:toView duration:0.5 options:(controllerIndex > tabBarController.selectedIndex ? UIViewAnimationOptionTransitionCurlUp : UIViewAnimationOptionTransitionCurlDown) completion:^(BOOL finished) { if (finished) { tabBarController.selectedIndex = controllerIndex; } }]; 

Soluzione 2: scorrimento (più complesso)

Una soluzione più complessa, ma offre un maggiore controllo dell’animazione. In questo esempio, le viste vengono visualizzate e distriggerste. Con questo, dobbiamo gestire noi stessi le opinioni.

 // Get the views. UIView * fromView = tabBarController.selectedViewController.view; UIView * toView = [[tabBarController.viewControllers objectAtIndex:controllerIndex] view]; // Get the size of the view area. CGRect viewSize = fromView.frame; BOOL scrollRight = controllerIndex > tabBarController.selectedIndex; // Add the to view to the tab bar view. [fromView.superview addSubview:toView]; // Position it off screen. toView.frame = CGRectMake((scrollRight ? 320 : -320), viewSize.origin.y, 320, viewSize.size.height); [UIView animateWithDuration:0.3 animations: ^{ // Animate the views on and off the screen. This will appear to slide. fromView.frame =CGRectMake((scrollRight ? -320 : 320), viewSize.origin.y, 320, viewSize.size.height); toView.frame =CGRectMake(0, viewSize.origin.y, 320, viewSize.size.height); } completion:^(BOOL finished) { if (finished) { // Remove the old view from the tabbar view. [fromView removeFromSuperview]; tabBarController.selectedIndex = controllerIndex; } }]; 

Questa soluzione in Swift:

 extension TabViewController: UITabBarControllerDelegate { public func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool { let fromView: UIView = tabBarController.selectedViewController!.view let toView : UIView = viewController.view if fromView == toView { return false } UIView.transitionFromView(fromView, toView: toView, duration: 0.3, options: UIViewAnimationOptions.TransitionCrossDissolve) { (finished:Bool) in } return true } } 

di seguito è il mio tentativo di utilizzare il modulo di codice drekka nel metodo delegato (UITabBarControllerDelegate)

 - (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController { NSArray *tabViewControllers = tabBarController.viewControllers; UIView * fromView = tabBarController.selectedViewController.view; UIView * toView = viewController.view; if (fromView == toView) return false; NSUInteger fromIndex = [tabViewControllers indexOfObject:tabBarController.selectedViewController]; NSUInteger toIndex = [tabViewControllers indexOfObject:viewController]; [UIView transitionFromView:fromView toView:toView duration:0.3 options: toIndex > fromIndex ? UIViewAnimationOptionTransitionFlipFromLeft : UIViewAnimationOptionTransitionFlipFromRight completion:^(BOOL finished) { if (finished) { tabBarController.selectedIndex = toIndex; } }]; return true; } 

La mia soluzione per iOS7.0 o superiore.

È ansible specificare un controller di animazione personalizzato nel delegato della barra delle tabs.

Implementa un controller di animazione come questo:

 @interface TabSwitchAnimationController : NSObject  @end @implementation TabSwitchAnimationController - (NSTimeInterval)transitionDuration:(id )transitionContext { return 0.2; } - (void)animateTransition:(id )transitionContext { UIViewController* fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIViewController* toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIView* toView = toVC.view; UIView* fromView = fromVC.view; UIView* containerView = [transitionContext containerView]; [containerView addSubview:toView]; toView.frame = [transitionContext finalFrameForViewController:toVC]; // Animate by fading toView.alpha = 0.0; [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction animations:^{ toView.alpha = 1.0; } completion:^(BOOL finished) { toView.alpha = 1.0; [fromView removeFromSuperview]; [transitionContext completeTransition:YES]; }]; } @end 

Quindi utilizzalo nel tuo UITabBarControllerDelegate:

 - (id )tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { return [[TabSwitchAnimationController alloc] init]; } 

Penso che puoi facilmente ottenere transizioni per UITabBarControlelr usando CATransition; Questo risolverà anche qualsiasi effetto collaterale dell’uso di transitionFromView: toView:

Utilizzalo all’interno della tua class TabBarController personalizzata estesa da UITabBarController.

 - (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController (UIViewController*)viewController { CATransition *animation = [CATransition animation]; [animation setType:kCATransitionFade]; [animation setDuration:0.25]; [animation setTimingFunction:[CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn]]; [self.view.window.layer addAnimation:animation forKey:@"fadeTransition"]; } 

Spero che questo ti aiuti 🙂

Ho scritto un post dopo aver provato le varie risposte qui.

Il codice è in Swift e puoi modificare la scheda in modo programmatico con l’animazione chiamando animateToTab .

 func animateToTab(toIndex: Int) { let tabViewControllers = viewControllers! let fromView = selectedViewController!.view let toView = tabViewControllers[toIndex].view let fromIndex = tabViewControllers.indexOf(selectedViewController!) guard fromIndex != toIndex else {return} // Add the toView to the tab bar view fromView.superview!.addSubview(toView) // Position toView off screen (to the left/right of fromView) let screenWidth = UIScreen.mainScreen().bounds.size.width; let scrollRight = toIndex > fromIndex; let offset = (scrollRight ? screenWidth : -screenWidth) toView.center = CGPoint(x: fromView.center.x + offset, y: toView.center.y) // Disable interaction during animation view.userInteractionEnabled = false UIView.animateWithDuration(0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: { // Slide the views by -offset fromView.center = CGPoint(x: fromView.center.x - offset, y: fromView.center.y); toView.center = CGPoint(x: toView.center.x - offset, y: toView.center.y); }, completion: { finished in // Remove the old view from the tabbar view. fromView.removeFromSuperview() self.selectedIndex = toIndex self.view.userInteractionEnabled = true }) } 

Se vuoi che tutte le modifiche alle tabs abbiano l’animazione, UITabBarControllerDelegate a UITabBarControllerDelegate come tale:

 func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool { let tabViewControllers = tabBarController.viewControllers! guard let toIndex = tabViewControllers.indexOf(viewController) else { return false } // Our method animateToTab(toIndex) return true } 

Invece di usare tabBarController:shouldSelectViewController: è meglio implementare tabBarController:animationControllerForTransitionFromViewController:toViewController:

TransitioningObject.swift

 import UIKit class TransitioningObject: NSObject, UIViewControllerAnimatedTransitioning { func animateTransition(transitionContext: UIViewControllerContextTransitioning) { let fromView: UIView = transitionContext.viewForKey(UITransitionContextFromViewKey)! let toView: UIView = transitionContext.viewForKey(UITransitionContextToViewKey)! transitionContext.containerView().addSubview(fromView) transitionContext.containerView().addSubview(toView) UIView.transitionFromView(fromView, toView: toView, duration: transitionDuration(transitionContext), options: UIViewAnimationOptions.TransitionCrossDissolve) { finished in transitionContext.completeTransition(true) } } func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { return 0.25 } } 

TabBarViewController.swift

 import UIKit class TabBarViewController: UITabBarController, UITabBarControllerDelegate { override func viewDidLoad() { super.viewDidLoad() self.delegate = self } // MARK: - Tabbar delegate func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { return TransitioningObject() } } 

La mia soluzione in Swift:

Crea una class TabBar personalizzata e impostala nella scheda TabBar dello storyboard

 class MainTabBarController: UITabBarController, UITabBarControllerDelegate { override func viewDidLoad() { super.viewDidLoad() self.delegate = self // Do any additional setup after loading the view. } func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool { let tabViewControllers = tabBarController.viewControllers! let fromView = tabBarController.selectedViewController!.view let toView = viewController.view if (fromView == toView) { return false } let fromIndex = tabViewControllers.indexOf(tabBarController.selectedViewController!) let toIndex = tabViewControllers.indexOf(viewController) let offScreenRight = CGAffineTransformMakeTranslation(toView.frame.width, 0) let offScreenLeft = CGAffineTransformMakeTranslation(-toView.frame.width, 0) // start the toView to the right of the screen if (toIndex < fromIndex) { toView.transform = offScreenLeft fromView.transform = offScreenRight } else { toView.transform = offScreenRight fromView.transform = offScreenLeft } fromView.tag = 124 toView.addSubview(fromView) self.view.userInteractionEnabled = false UIView.animateWithDuration(0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: { toView.transform = CGAffineTransformIdentity }, completion: { finished in let subViews = toView.subviews for subview in subViews{ if (subview.tag == 124) { subview.removeFromSuperview() } } tabBarController.selectedIndex = toIndex! self.view.userInteractionEnabled = true }) return true } } 

una correzione per l’animazione vivace …

UIView * fromView = self.view.superview;

questo può essere risolto in due modi

1 – Scrivi questo nel tuo file AppDelegate.m una volta. Ricordati di includere UITabBarControllerDelegate usando <> dopo i due punti (:) nel tuo AppDelegate.h

 -(void)tabBarController:(UITabBarController *)tabBarControllerThis didSelectViewController:(UIViewController *)viewController { [UIView transitionWithView:viewController.view duration:0.1 options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionTransitionCrossDissolve animations:^(void){ } completion:^(BOOL finished){ [UIView beginAnimations:@"animation" context:nil]; [UIView setAnimationDuration:0.7]; [UIView setAnimationBeginsFromCurrentState:YES]; [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:viewController.view cache:NO]; [UIView commitAnimations]; }]; } 

2 – Scrivilo in ognuno dei tuoi file ViewController.m

 -(void)viewWillAppear:(BOOL)animated { [UIView transitionWithView:self.view duration:1.0 options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionTransitionCrossDissolve animations:^(void){ [super viewWillAppear:YES]; } completion:^(BOOL finished){ }]; } 

spero che questo aiuto …!

Ho usato la soluzione di @Mofumofu e l’ho aggiornata a Swift 1.2 e ho anche implementato un’animazione su / giù. Significa che il nuovo ViewController si presenta e spinge il vecchio verso l’alto se l’indice del nuovo viewcontroller è maggiore di quello precedente. Altrimenti la direzione è giù.

 class TabScrollPageAnimationController: NSObject, UIViewControllerAnimatedTransitioning { let tabBarController: UITabBarController init(tabBarController: UITabBarController) { self.tabBarController = tabBarController } func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { return 0.5 } func animateTransition(transitionContext: UIViewControllerContextTransitioning) { if let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey), let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) { let fromView = fromVC.view let toView = toVC.view let containerView = transitionContext.containerView() var directionUpwardMultiplier: CGFloat = 1.0 if let vcs = tabBarController.viewControllers as? [UIViewController], let fIndex = find(vcs, fromVC), let tIndex = find(vcs, toVC) { directionUpwardMultiplier = (fIndex < tIndex) ? +1.0 : -1.0 } containerView.clipsToBounds = false containerView.addSubview(toView) var fromViewEndFrame = fromView.frame fromViewEndFrame.origin.y -= (containerView.frame.height * directionUpwardMultiplier) let toViewEndFrame = transitionContext.finalFrameForViewController(toVC) var toViewStartFrame = toViewEndFrame toViewStartFrame.origin.y += (containerView.frame.height * directionUpwardMultiplier) toView.frame = toViewStartFrame toView.alpha = 0.0 UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in toView.alpha = 1.0 toView.frame = toViewEndFrame fromView.alpha = 0.0 fromView.frame = fromViewEndFrame }, completion: { (completed) -> Void in toView.alpha = 1.0 fromView.removeFromSuperview() transitionContext.completeTransition(completed) containerView.clipsToBounds = true }) } } } 

Nel contenitore ViewController:

 extension XYViewController: UITabBarControllerDelegate { func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { return TabScrollPageAnimationController(tabBarController: tabBarController) } } 

la risposta di drekka è davvero grandiosa. Ho modificato leggermente la transizione di scorrimento in modo che l’animazione assomigliasse più all’animazione push di Apple. Ho aggiunto un’animazione aggiuntiva al completamento della prima animazione per ottenere quell’effetto scorrevole che sembrava giusto.

 // Disable interaction during animation to avoids bugs. self.tabBarController.view.userInteractionEnabled = NO; // Get the views. UIView * fromView = tabBarController.selectedViewController.view; UIView * toView = [[tabBarController.viewControllers objectAtIndex:controllerIndex] view]; // Get the size of the view area. CGRect viewSize = fromView.frame; BOOL scrollRight = controllerIndex > tabBarController.selectedIndex; // Add the to view to the tab bar view. [fromView.superview addSubview:toView]; [fromView.superview addSubview:fromView]; self.tabBarController.selectedIndex = 0; // Position it off screen. toView.frame = CGRectMake((scrollRight ? (viewSize.size.width *.25) : -(viewSize.size.width * .25 )), viewSize.origin.y, viewSize.size.width, viewSize.size.height); [UIView animateWithDuration:0.25 animations: ^{ // Animate the views on and off the screen. [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; fromView.frame = CGRectMake(viewSize.size.width * .95, viewSize.origin.y, viewSize.size.width, viewSize.size.height); toView.frame = CGRectMake((viewSize.origin.x * .90), viewSize.origin.y, viewSize.size.width, viewSize.size.height); } completion:^(BOOL finished) { if (finished) { // Being new animation. [UIView animateWithDuration:0.2 animations: ^{ [UIView setAnimationCurve:UIViewAnimationCurveLinear]; fromView.frame = CGRectMake(viewSize.size.width, viewSize.origin.y, viewSize.size.width, viewSize.size.height); toView.frame = CGRectMake((viewSize.origin.x), viewSize.origin.y, viewSize.size.width, viewSize.size.height); } completion:^(BOOL finished) { if (finished) { // Remove the old view from the tabbar view. [fromView removeFromSuperview]; // Restore interaction. self.tabBarController.view.userInteractionEnabled = YES; } }]; } }]; 

Ecco la mia soluzione Swift 3:

Sovrascrivo selectedIndex del mio UITabBarViewController in questo modo:

 override var selectedIndex: Int{ get{ return super.selectedIndex } set{ animateToTab(toIndex: newValue) super.selectedIndex = newValue } } 

Quindi uso questa funzione che riproduce l’animazione push / pop nativa:

 func animateToTab(toIndex: Int) { guard let tabViewControllers = viewControllers, tabViewControllers.count > toIndex, let fromViewController = selectedViewController, let fromIndex = tabViewControllers.index(of: fromViewController), fromIndex != toIndex else {return} view.isUserInteractionEnabled = false let toViewController = tabViewControllers[toIndex] let push = toIndex > fromIndex let bounds = UIScreen.main.bounds let offScreenCenter = CGPoint(x: fromViewController.view.center.x + bounds.width, y: toViewController.view.center.y) let partiallyOffCenter = CGPoint(x: fromViewController.view.center.x - bounds.width*0.25, y: fromViewController.view.center.y) if push{ fromViewController.view.superview?.addSubview(toViewController.view) toViewController.view.center = offScreenCenter }else{ fromViewController.view.superview?.insertSubview(toViewController.view, belowSubview: fromViewController.view) toViewController.view.center = partiallyOffCenter } UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: .curveEaseIn, animations: { toViewController.view.center = fromViewController.view.center fromViewController.view.center = push ? partiallyOffCenter : offScreenCenter }, completion: { finished in fromViewController.view.removeFromSuperview() self.view.isUserInteractionEnabled = true }) } 

Spero possa essere d’aiuto 🙂

Puoi animare a seconda dell’object toccato – In questo esempio abbiamo flipFromLeft se l’indice TAPpato è> rispetto all’indice selezionato precedente e noi flipFromRight se l’indice TAPpato è

 func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { let fromView: UIView = tabBarController.selectedViewController!.view let toView: UIView = viewController.view if fromView == toView { return false } if let tappedIndex = tabBarController.viewControllers?.index(of: viewController) { if tappedIndex > tabBarController.selectedIndex { UIView.transition(from: fromView, to: toView, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromLeft, completion: nil) } else { UIView.transition(from: fromView, to: toView, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromRight, completion: nil) } } return true } 

Volevo utilizzare una transizione di flip tra due controller di visualizzazione figlio su una pressione di un pulsante e l’ho ottenuta come segue:

 -(IBAction)flipViewControllers:(id)sender{ NSUInteger index = self.selectedIndex; index++; if(index >= self.childViewControllers.count){ index = 0; } self.selectedIndex = index; [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.75]; [UIView setAnimationTransition:index % 2 ? UIViewAnimationTransitionFlipFromLeft : UIViewAnimationTransitionFlipFromRight forView:self.view cache:YES]; [UIView commitAnimations]; } 

Ho anche impostato il colore di sfondo sul nero, nel mio caso l’ho fatto impostando navigationController.view.backgroundColor ma nel tuo caso potrebbe essere il window.backgroundColor che può essere facilmente impostato nel delegato dell’app.

Ecco il mio codice di lavoro ( per 3 tabs , non l’ho provato più!) Per animare le transizioni tra le tabs. Si basa principalmente sulla soluzione di drekka, ma già implementata nel metodo delegate della tabbar, quindi dovrebbe fare il lavoro se si copia / incolla semplicemente .. (non si sa mai!)

 -(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController { // Important! We validate that the selected tab is not the current tab, to avoid misplacing views if (tabBarController.selectedViewController == viewController) { return NO; } // Find the selected view's index NSUInteger controllerIndex = 0; for (UIViewController *vc in tabBarController.viewControllers) { if (vc == viewController) { controllerIndex = [tabBarController.viewControllers indexOfObject:vc]; } } CGFloat screenWidth = SCREEN_SIZE.width; // Note: We must invert the views according to the direction of the scrolling ( FROM Left TO right or FROM right TO left ) UIView * fromView = tabBarController.selectedViewController.view; UIView * toView = viewController.view; [fromView.superview addSubview:toView]; CGRect fromViewInitialFrame = fromView.frame; CGRect fromViewNewframe = fromView.frame; CGRect toViewInitialFrame = toView.frame; if ( controllerIndex > tabBarController.selectedIndex ) { // FROM left TO right ( tab0 to tab1 or tab2 ) // The final frame for the current view. It will be displaced to the left fromViewNewframe.origin.x = -screenWidth; // The initial frame for the new view. It will be displaced to the left toViewInitialFrame.origin.x = screenWidth; toView.frame = toViewInitialFrame; } else { // FROM right TO left ( tab2 to tab1 or tab0 ) // The final frame for the current view. It will be displaced to the right fromViewNewframe.origin.x = screenWidth; // The initial frame for the new view. It will be displaced to the right toViewInitialFrame.origin.x = -screenWidth; toView.frame = toViewInitialFrame; } [UIView animateWithDuration:0.2 animations:^{ // The new view will be placed where the initial view was placed toView.frame = fromViewInitialFrame; // The initial view will be place outside the screen bounds fromView.frame = fromViewNewframe; tabBarController.selectedIndex = controllerIndex; // To prevent user interaction during the animation [[UIApplication sharedApplication] beginIgnoringInteractionEvents]; } completion:^(BOOL finished) { // Before removing the initial view, we adjust its frame to avoid visual lags fromView.frame = CGRectMake(0, 0, fromView.frame.size.width, fromView.frame.size.height); [fromView removeFromSuperview]; [[UIApplication sharedApplication] endIgnoringInteractionEvents]; }]; return NO; 

}

Questo funziona per me in Swift 3:

 func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { if let fromView = tabBarController.selectedViewController?.view, let toView = viewController.view { if fromView == toView { return false } UIView.transition(from: fromView, to: toView, duration: 0.2, options: .transitionCrossDissolve) { (finished) in } } return true } 

@samwize La risposta è stata tradotta in Swift 3 – 2 pollici in su su questo, crea un effetto left to wright della pagina:

 func animateToTab(toIndex: Int) { let tabViewControllers = viewControllers! let fromView = selectedViewController!.view let toView = tabViewControllers[toIndex].view let fromIndex = tabViewControllers.index(of: selectedViewController!) guard fromIndex != toIndex else {return} // Add the toView to the tab bar view fromView?.superview!.addSubview(toView!) // Position toView off screen (to the left/right of fromView) let screenWidth = screenSize.width let scrollRight = toIndex > fromIndex! let offset = (scrollRight ? screenWidth : -screenWidth) toView?.center = CGPoint(x: (fromView?.center.x)! + offset, y: (toView?.center.y)!) // Disable interaction during animation view.isUserInteractionEnabled = false UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIViewAnimationOptions.curveEaseOut, animations: { // Slide the views by -offset fromView?.center = CGPoint(x: (fromView?.center.x)! - offset, y: (fromView?.center.y)!); toView?.center = CGPoint(x: (toView?.center.x)! - offset, y: (toView?.center.y)!); }, completion: { finished in // Remove the old view from the tabbar view. fromView?.removeFromSuperview() self.selectedIndex = toIndex self.view.isUserInteractionEnabled = true }) } 

Swift 4+

Il metodo UITabBarControllerDelegate dovrebbe essere come questo,

 func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { animateToTab(toIndex: (tabBarController.viewControllers?.index(of: viewController))!) return true } 

E il metodo è,

 func animateToTab(toIndex: Int) { let tabViewControllers = viewControllers! let fromView = selectedViewController!.view let toView = tabViewControllers[toIndex].view let fromIndex = tabViewControllers.index(of: selectedViewController!) guard fromIndex != toIndex else {return} // Add the toView to the tab bar view fromView!.superview!.addSubview(toView!) // Position toView off screen (to the left/right of fromView) let screenWidth = UIScreen.main.bounds.size.width; let scrollRight = toIndex > fromIndex!; let offset = (scrollRight ? screenWidth : -screenWidth) toView!.center = CGPoint(x: fromView!.center.x + offset, y: toView!.center.y) // Disable interaction during animation view.isUserInteractionEnabled = false UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: UIViewAnimationOptions.curveEaseOut, animations: { // Slide the views by -offset fromView!.center = CGPoint(x: fromView!.center.x - offset, y: fromView!.center.y); toView!.center = CGPoint(x: toView!.center.x - offset, y: toView!.center.y); }, completion: { finished in // Remove the old view from the tabbar view. fromView!.removeFromSuperview() self.selectedIndex = toIndex self.view.isUserInteractionEnabled = true }); }