Tengo una aplicación en la que necesito eliminar una vista de la pila de un UINavigationController y reemplazarla por otra. La situación es que la primera vista crea un elemento editable y luego se reemplaza con un editor para el elemento. Cuando hago la solución obvia en la primera vista:
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];
[self retain];
[self.navigationController popViewControllerAnimated: NO];
[self.navigationController pushViewController: mevc animated: YES];
[self release];
Tengo un comportamiento muy extraño. Por lo general, aparecerá la vista del editor, pero si trato de usar el botón Atrás en la barra de navegación, obtengo pantallas adicionales, algunas en blanco y otras simplemente arruinadas. El título también se vuelve aleatorio. Es como si la pila de navegación estuviera completamente regada.
¿Cuál sería un mejor enfoque para este problema?
Gracias Matt
fuente
El siguiente enfoque me parece más agradable y también funciona bien con ARC:
UIViewController *newVC = [[UIViewController alloc] init]; // Replace the current view controller NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]]; [viewControllers removeLastObject]; [viewControllers addObject:newVC]; [[self navigationController] setViewControllers:viewControllers animated:YES];
fuente
if(indexPath.row == 0){UIViewController *newVC = [[UIViewController alloc] init];newVC = [self.storyboard instantiateViewControllerWithIdentifier:@"Item1VC"]; NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[_detailViewController.navigationController viewControllers]]; [viewControllers removeLastObject];[viewControllers addObject:newVC]; [_detailViewController.navigationController setViewControllers:viewControllers animated:YES];}
Por experiencia, tendrá que jugar
viewControllers
directamente con la propiedad de UINavigationController . Algo como esto debería funcionar:MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo]; [[self retain] autorelease]; NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease]; [controllers removeLastObject]; self.navigationController.viewControllers = controllers; [self.navigationController pushViewController:mevc animated: YES];
Nota: Cambié la retención / liberación a retención / liberación automática, ya que generalmente es más robusta: si ocurre una excepción entre la retención / liberación, se filtrará usted mismo, pero la liberación automática se encarga de eso.
fuente
Después de mucho esfuerzo (y de modificar el código de Kevin), finalmente descubrí cómo hacer esto en el controlador de vista que se está extrayendo de la pila. El problema que estaba teniendo era que self.navigationController devolvía nil después de que eliminé el último objeto de la matriz de controladores. Creo que se debió a esta línea en la documentación de UIViewController en el método de instancia navigationController "Solo devuelve un controlador de navegación si el controlador de vista está en su pila".
Creo que una vez que el controlador de vista actual se elimina de la pila, su método navigationController devolverá nil.
Aquí está el código ajustado que funciona:
UINavigationController *navController = self.navigationController; MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo]; NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease]; [controllers removeLastObject]; navController.viewControllers = controllers; [navController pushViewController:mevc animated: YES];
fuente
Gracias, esto era exactamente lo que necesitaba. También puse esto en una animación para obtener la curvatura de la página:
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo]; UINavigationController *navController = self.navigationController; [[self retain] autorelease]; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration: 0.7]; [UIView setAnimationTransition:<#UIViewAnimationTransitionCurlDown#> forView:navController.view cache:NO]; [navController popViewControllerAnimated:NO]; [navController pushViewController:mevc animated:NO]; [UIView commitAnimations];
0,6 de duración es rápido, bueno para 3GS y versiones posteriores, 0,8 sigue siendo un poco demasiado rápido para 3G.
Johan
fuente
Si desea mostrar cualquier otro controlador de vista por popToRootViewController, debe hacer lo siguiente:
UIViewController *newVC = [[WelcomeScreenVC alloc] initWithNibName:@"WelcomeScreenVC" bundle:[NSBundle mainBundle]]; NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]]; [viewControllers removeAllObjects]; [viewControllers addObject:newVC]; [[self navigationController] setViewControllers:viewControllers animated:NO];
Ahora, se eliminará toda su pila anterior y se creará una nueva pila con su rootViewController requerido.
fuente
Tuve que hacer algo similar recientemente y basé mi solución en la respuesta de Michaels. En mi caso, tuve que eliminar dos controladores de vista de la pila de navegación y luego agregar un nuevo controlador de vista. Vocación
dos veces, funcionó bien en mi caso.UINavigationController *navController = self.navigationController; // retain ourselves so that the controller will still exist once it's popped off [[self retain] autorelease]; searchViewController = [[SearchViewController alloc] init]; NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease]; [controllers removeLastObject]; // In my case I want to go up two, then push one.. [controllers removeLastObject]; navController.viewControllers = controllers; NSLog(@"controllers: %@",controllers); controllers = nil; [navController pushViewController:searchViewController animated: NO];
fuente
Este
UINavigationController
método de instancia podría funcionar ...Aparece los controladores de vista hasta que el controlador de vista especificado sea el controlador de vista superior y luego actualice la pantalla.
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
fuente
Aquí hay otro enfoque que no requiere jugar directamente con la matriz viewControllers. Verifique si el controlador ya ha aparecido, si es así, presiónelo.
TasksViewController *taskViewController = [[TasksViewController alloc] initWithNibName:nil bundle:nil]; if ([navigationController.viewControllers indexOfObject:taskViewController] == NSNotFound) { [navigationController pushViewController:taskViewController animated:animated]; } else { [navigationController popToViewController:taskViewController animated:animated]; }
fuente
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy]; for(int i=0;i<controllers.count;i++){ [controllers removeLastObject]; } self.navigationController.viewControllers = controllers;
fuente
Mi forma favorita de hacerlo es con una categoría en UINavigationController. Lo siguiente debería funcionar:
UINavigationController + Helpers.h #import
@interface UINavigationController (Helpers) - (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller; @end
UINavigationController + Helpers.m
#import "UINavigationController + Helpers.h"
@implementation UINavigationController (Helpers) - (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller { UIViewController* topController = self.viewControllers.lastObject; [[topController retain] autorelease]; UIViewController* poppedViewController = [self popViewControllerAnimated:NO]; [self pushViewController:controller animated:NO]; return poppedViewController; } @end
Luego, desde su controlador de vista, puede reemplazar la vista superior con una nueva de esta manera:
[self.navigationController replaceTopViewControllerWithViewController: newController];
fuente
Puede verificar con la matriz de controladores de vista de navegación que le brinda todos los controladores de vista que ha agregado en la pila de navegación. Al usar esa matriz, puede volver a navegar al controlador de vista específico.
fuente
Para IOS monotouch / xamarin:
dentro de la clase UISplitViewController;
UINavigationController mainNav = this._navController; //List<UIViewController> controllers = mainNav.ViewControllers.ToList(); mainNav.ViewControllers = new UIViewController[] { }; mainNav.PushViewController(detail, true);//to have the animation
fuente
Alternativamente,
Puedes usar
category
para evitarself.navigationController
sernil
despuéspopViewControllerAnimated
simplemente abre y presiona, es fácil de entender, no es necesario acceder
viewControllers
...// UINavigationController+Helper.h @interface UINavigationController (Helper) - (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated; @end // UINavigationController+Helper.m @implementation UINavigationController (Helper) - (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated { UIViewController *v =[self popViewControllerAnimated:NO]; [self pushViewController:viewController animated:animated]; return v; } @end
En su ViewController
// #import "UINavigationController+Helper.h" // invoke in your code UIViewController *v= [[MyNewViewController alloc] init]; [self.navigationController popThenPushViewController:v animated:YES]; RELEASE_SAFELY(v);
fuente
No es exactamente la respuesta, pero podría ser de ayuda en algunos escenarios (el mío, por ejemplo):
Si necesita abrir el controlador de vista C e ir a B (fuera de la pila) en lugar de A (el que está debajo de C), es posible presionar B antes de C y tener los 3 en la pila. Al mantener invisible el empuje B, y al elegir si hacer estallar solo C o C y B por completo, puede lograr el mismo efecto.
problema inicial A -> C (quiero sacar C y mostrar B, fuera de la pila)
posible solución A -> B (empujado invisible) -> C (cuando abro C, elijo mostrar B o también hacerlo)
fuente
Utilizo esta solución para mantener la animación.
[self.navigationController pushViewController:controller animated:YES]; NSMutableArray *newControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers]; [newControllers removeObject:newControllers[newControllers.count - 2]]; [self.navigationController setViewControllers:newControllers];
fuente