iphone - sacar - ¿Cómo puedo abrir una vista desde un UINavigationController y reemplazarla por otra en una sola operación?
pila iphone 5s (16)
Alternativamente,
Puede usar category
para evitar que self.navigationController
sea nil
después de popViewControllerAnimated
simplemente pop y push, es fácil de entender, no necesita acceder a 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);
Tengo una aplicación donde 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, la vista del editor aparecerá, pero si trato de usar el botón Atrás en la barra de navegación, obtengo pantallas adicionales, algunas en blanco y algunas simplemente jodidas. 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
Aquí hay otro enfoque que no requiere jugar directamente con la matriz viewControllers. Verifique si el controlador se ha apagado aún, de ser así, empújelo.
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];
}
Descubrí que no necesitas desordenar manualmente la propiedad viewControllers
. Básicamente hay 2 cosas difíciles sobre esto.
-
self.navigationController
devolveránil
siself
no está actualmente en la pila del controlador de navegación. Así que guárdelo en una variable local antes de perder acceso a él. - Debe
retain
(yrelease
adecuadamente)self
o el objeto que posee el método en el que se encuentra será desasignado, causando extrañeza.
Una vez que hagas esa preparación, simplemente presiona y empuja como siempre. Este código reemplazará instantáneamente el controlador superior por otro.
// locally store the navigation controller since
// self.navigationController will be nil once we are popped
UINavigationController *navController = self.navigationController;
// retain ourselves so that the controller will still exist once it''s popped off
[[self retain] autorelease];
// Pop this controller and replace with another
[navController popViewControllerAnimated:NO];
[navController pushViewController:someViewController animated:NO];
En esa última línea si cambia el modo animated
a YES
, entonces la nueva pantalla se animará y el controlador que acaba de saltar se animará. Se ve muy bien!
Después de mucho esfuerzo (y ajustar el código de Kevin), finalmente descubrí cómo hacer esto en el controlador de vista que se está sacando de la pila. El problema que tenía era que self.navigationController devolvía nada después de eliminar el último objeto de la matriz de controladores. Creo que fue debido 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 elimine de la pila, su método navigationController devolverá nulo.
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];
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];
Este método de instancia UINavigationController
podría funcionar ...
Muestra los controladores de vista hasta que el controlador de vista especificado sea el controlador de vista superior y luego actualiza la pantalla.
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
Gracias, esto era exactamente lo que necesitaba. También puse esto en una animación para obtener el curl 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];
La duración 0.6 es rápida, buena para 3GS y más reciente, 0.8 es demasiado rápido para 3G.
Johan
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 por una nueva de esta manera:
[self.navigationController replaceTopViewControllerWithViewController: newController];
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 que C y tener los 3 en la pila. Al mantener el empuje B invisible, y al elegir si mostrar solo C o C y B en conjunto, puede lograr el mismo efecto.
problema inicial A -> C (Quiero mostrar C y mostrar B, fuera de la pila)
posible solución A -> B (invisible) -> C (cuando hago clic en C, elijo mostrar B o también pop)
Para monotouch / xamarin IOS:
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
Por experiencia, tendrá que jugar con la propiedad viewControllers de viewControllers
directamente. 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é el retener / liberar a un retener / liberar automáticamente, ya que en general es más robusto: si ocurre una excepción entre retener / liberar, se perderá, pero la liberación automática se ocupa de eso.
Puede verificar con la matriz de controladores de vista de navegación que le proporciona todos los controladores de vista que ha agregado en la pila de navegación. Al usar esa matriz, puede retroceder a un controlador de vista específico.
Si desea mostrar cualquier otro controlador de vista mediante 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 tu pila anterior y se creará una nueva pila con tu rootViewController requerido.
Tuve que hacer algo similar recientemente y basé mi solución en la respuesta de Michaels. En mi caso, tuve que quitar dos Controladores de Vista de la Pila de navegación y luego agregar un nuevo Controlador de Vista. Vocación
[controllers removeLastObject]; 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];
Yo uso 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];
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy];
for(int i=0;i<controllers.count;i++){
[controllers removeLastObject];
}
self.navigationController.viewControllers = controllers;