ios4 - ventanas - efecto parallax iphone 6
iPhone: ¿Cómo cambiar pestañas con una animación? (18)
Swift 4+
El método UITabBarControllerDelegate
debería ser así,
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
animateToTab(toIndex: (tabBarController.viewControllers?.index(of: viewController))!)
return true
}
Y el método es,
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
});
}
Estoy cambiando las pestañas programáticamente en una aplicación impulsada por barra de pestañas utilizando UITabBarController.selectedIndex
. El problema que trato de resolver es cómo animar la transición entre las vistas. es decir. desde la vista de la pestaña actual hasta la vista de la pestaña seleccionada.
La primera idea fue hacer uso de UITabBarControllerDelegate
, pero parece que no se llama cuando se cambian las pestañas mediante programación. Ahora estoy considerando el UITabBarDelegate.didSelectItem
: como un posible gancho para establecer una animación de transición.
¿Alguien ha logrado animar las transiciones? Si es así, ¿cómo?
@samwize Respuesta traducida a Swift 3 - 2 pulgares arriba en este, crea un efecto de página de izquierda a derecha:
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
})
}
Aquí está mi código de trabajo (¡ para 3 pestañas , no lo he probado más!) Para animar las transiciones entre las pestañas. Se basa principalmente en la solución de drekka, pero ya se implementó en el método de delegado tabbar, por lo que debería hacer el trabajo si solo lo copia / pega ... (¡nunca se sabe!)
-(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;
}
Aquí está mi solución Swift 3:
Anulo selectedIndex de mi UITabBarViewController de esta manera:
override var selectedIndex: Int{
get{
return super.selectedIndex
}
set{
animateToTab(toIndex: newValue)
super.selectedIndex = newValue
}
}
Luego uso esta función que imita la animación nativa push / pop:
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
})
}
Espero que ayude :)
Creo que puede lograr fácilmente transiciones para UITabBarControlelr usando CATransition; Esto también resolverá los efectos secundarios del uso de transitionFromView: toView:
Use esto dentro de su clase personalizada TabBarController extendida desde 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"];
}
Espero que esto ayude :)
En lugar de usar tabBarController:shouldSelectViewController:
es mejor implementar 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()
}
}
Escribí una post después de probar las diversas respuestas aquí.
El código está en Swift, y puede cambiar programáticamente la pestaña con animación llamando a 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
})
}
Si desea que todas las pestañas cambien para tener la animación, enganche a UITabBarControllerDelegate
como tal:
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
}
Esto funciona para mí en 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
}
La respuesta de Drekka es realmente genial. Ajusté un poco la transición de desplazamiento para que la animación se pareciera más a la animación de empuje de Apple. Agregué una animación adicional al completar la primera animación para que el efecto deslizante se viera bien.
// 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;
}
}];
}
}];
Lo que sigue es mi intento de usar el código de drekka en el método de delegado (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;
}
Mi solución en Swift:
Crea una clase de TabBar personalizada y configúrala en tu guión gráfico TabBar
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
}
}
Mi solución para iOS7.0 o superior.
Puede especificar un controlador de animación personalizado en el delegado de la barra de pestañas.
Implementa un controlador de animación como este:
@interface TabSwitchAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
@end
@implementation TabSwitchAnimationController
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.2;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)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
Luego úsala en tu UITabBarControllerDelegate:
- (id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
return [[TabSwitchAnimationController alloc] init];
}
Puede animar dependiendo del elemento tocado: en este ejemplo, volteamos desde la izquierda si el índice tocado es> que el índice seleccionado previamente y volvemos de derecha a derecha si el índice tocado es <que el índice seleccionado previamente. Esto es Swift 4: Implemente el método UITabBarControllerDelegate
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
}
Quería utilizar una transición flip entre dos controles de vista infantil en un botón de prensa y lo conseguí de la siguiente manera:
-(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];
}
También configuré el color de fondo en negro, en mi caso lo hice estableciendo el navigationController.view.backgroundColor pero en su caso podría ser el window.backgroundColor que se puede establecer fácilmente en el delegado de la aplicación.
Usé la solución de @Mofumofu y la actualicé a Swift 1.2 y también implementé una animación hacia arriba y hacia abajo. Es decir, aparece el nuevo ViewController y empuja el anterior si el índice del nuevo viewcontroller es mayor que el anterior. De lo contrario, la dirección está baja.
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
})
}
}
}
En el Container ViewController:
extension XYViewController: UITabBarControllerDelegate {
func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return TabScrollPageAnimationController(tabBarController: tabBarController)
}
}
esto se puede resolver de dos maneras
1 - Escribe esto en tu archivo AppDelegate.m una vez. Recuerde incluir UITabBarControllerDelegate usando <> después de dos puntos (:) en su 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 - Escribe esto en cada uno de tus archivos ViewController.m
-(void)viewWillAppear:(BOOL)animated
{
[UIView transitionWithView:self.view
duration:1.0
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionTransitionCrossDissolve
animations:^(void){
[super viewWillAppear:YES];
} completion:^(BOOL finished){
}];
}
espero que esto ayude...!
una solución para la animación nerviosa ...
UIView * fromView = self.view.superview;
Actualización 04/2016: Justed quería actualizar esto para decir gracias a todos por todos los votos. También tenga en cuenta que esto fue escrito originalmente cuando ... antes de ARC, antes de restricciones, antes ... ¡muchas cosas! Por lo tanto, tenga esto en cuenta al decidir si utilizar estas técnicas. Puede haber enfoques más modernos. Ah, y si encuentras uno. Por favor, agregue una respuesta para que todos puedan ver. Gracias.
Algún tiempo después ...
Después de mucha investigación, se me ocurrió dos soluciones de trabajo. Ambos funcionaron e hicieron la animación entre pestañas.
Solución 1: transición de vista (simple)
Este es el más fácil y hace uso de un método de transición UIView predefinido. Con esta solución no necesitamos administrar las vistas porque el método nos funciona.
// 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;
}
}];
Solución 2: desplazamiento (más complejo)
Una solución más compleja, pero te da más control de la animación. En este ejemplo obtenemos las vistas para deslizar dentro y fuera. Con este necesitamos gestionar los puntos de vista nosotros mismos.
// 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;
}
}];
Esta solución en 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
}
}