para - Cambio del controlador de vista raíz de una ventana de iOS
ios 12 lanzamiento (5)
¿El controlador de vista raíz de una ventana de iOS normalmente se inicializa una vez al principio en un controlador de barra de pestañas o en un controlador de navegación? ¿Está bien cambiar el controlador de vista raíz varias veces dentro de una aplicación?
Tengo un escenario donde la vista superior es diferente en función de la acción del usuario. Estaba pensando en tener un controlador de navegación con el controlador de vista superior que tenga la imagen de la pantalla de bienvenida y empujar / abrir los controladores de vista según sea necesario. Alternativamente, puedo seguir cambiando el controlador de vista superior de la ventana. ¿Cuál será un mejor enfoque?
A partir de los comentarios sobre la respuesta de serge-k, he construido una solución de trabajo con una solución alternativa de comportamiento extraño cuando se presenta un controlador de vista modal sobre el antiguo controlador de raíz:
extension UIView {
func snapshot() -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale)
drawViewHierarchyInRect(bounds, afterScreenUpdates: true)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
}
extension UIWindow {
func replaceRootViewControllerWith(_ replacementController: UIViewController, animated: Bool, completion: (() -> Void)?) {
let snapshotImageView = UIImageView(image: self.snapshot())
self.addSubview(snapshotImageView)
let dismissCompletion = { () -> Void in // dismiss all modal view controllers
self.rootViewController = replacementController
self.bringSubview(toFront: snapshotImageView)
if animated {
UIView.animate(withDuration: 0.4, animations: { () -> Void in
snapshotImageView.alpha = 0
}, completion: { (success) -> Void in
snapshotImageView.removeFromSuperview()
completion?()
})
}
else {
snapshotImageView.removeFromSuperview()
completion?()
}
}
if self.rootViewController!.presentedViewController != nil {
self.rootViewController!.dismiss(animated: false, completion: dismissCompletion)
}
else {
dismissCompletion()
}
}
}
Para reemplazar el RootViewController simplemente use:
let newRootViewController = self.storyboard!.instantiateViewControllerWithIdentifier("BlackViewController")
UIApplication.sharedApplication().keyWindow!.replaceRootViewControllerWith(newRootViewController, animated: true, completion: nil)
Espero que esto ayude :) probado en iOS 8.4; también probado para el soporte de controladores de navegación (debería admitir también controladores de barra de pestañas, etc., pero no lo probé)
Explicación
Si hay un controlador de vista modal presentado sobre el viejo controlador raíz, se reemplaza el control raíz, pero la vista anterior permanece debajo de la vista del nuevo controlador raíz (y se puede ver, por ejemplo, durante las animaciones de transición Flip Horizontal o Cross Dissolve) y el antiguo controlador de vista la jerarquía permanece asignada (lo que puede causar graves problemas de memoria si el reemplazo ocurre varias veces).
Por lo tanto, la única solución es descartar todos los controladores de vista modal y luego reemplazar el control raízViewController. Una instantánea de la pantalla se coloca sobre la ventana durante el descarte y el reemplazo para ocultar el desagradable proceso de flasheo.
Para iOS8, también debemos establecer dos parámetros debajo de SÍ.
providesPresentationContextTransitionStyle
definesPresentationContext
Aquí está mi código para presentar el controlador de vista modelo transparente en el controlador de navegación para iOS 6 y superior.
ViewController *vcObj = [[ViewController alloc] initWithNibName:NSStringFromClass([ViewController class]) bundle:nil];
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:vcObj];
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
navCon.providesPresentationContextTransitionStyle = YES;
navCon.definesPresentationContext = YES;
navCon.modalPresentationStyle = UIModalPresentationOverCurrentContext;
[self presentViewController:navCon animated:NO completion:nil];
}
else {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[self presentViewController:navCon animated:NO completion:^{
[navCon dismissViewControllerAnimated:NO completion:^{
appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentViewController:navCon animated:NO completion:nil];
appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationFullScreen;
}];
}];
}
Puede cambiar el control raíz de la ventana a lo largo del ciclo de vida de la aplicación.
UIViewController *viewController = [UIViewController alloc] init];
[self.window setRootViewController:viewController];
Cuando cambie el control raízViewController, es posible que desee agregar un UIImageView como subvista en la ventana para actuar como una imagen splash. Espero que esto tenga sentido, algo como esto:
- (void) addSplash {
CGRect rect = [UIScreen mainScreen].bounds;
UIImageView *splashImage = [[UIImageView alloc] initWithFrame:rect];
splashImage.image = [UIImage imageNamed:@"splash.png"];
[self.window addSubview:splashImage];
}
- (void) removeSplash {
for (UIView *view in self.window.subviews) {
if ([view isKindOfClass:[UIImageView class]]) {
[view removeFromSuperview];
}
}
}
iOS 8.0, Xcode 6.0.1, ARC habilitado
La mayoría de tus preguntas fueron respondidas. Sin embargo, puedo abordar uno que tuve que enfrentar recientemente.
¿Está bien, cambiar el controlador de vista raíz varias veces, dentro de una aplicación?
La respuesta es sí . Tuve que hacer esto recientemente para restablecer mi jerarquía UIView después de las UIViews iniciales que formaban parte de la aplicación. comenzar a funcionar ya no era necesario. En otras palabras, puede restablecer su "controlador raíz de escritorio" desde cualquier otro UIViewController en cualquier momento después de la aplicación. "didFinishLoadingWithOptions".
Para hacer esto...
1) Declara una referencia a tu aplicación. delegado (aplicación llamada "Prueba") ...
TestAppDelegate *testAppDelegate = (TestAppDelegate *)[UIApplication sharedApplication].delegate;
2) Elige un UIViewController que desees que sea tu "rootViewController"; ya sea desde el guión gráfico o definir programáticamente ...
- a) guión gráfico (asegúrese de que el identificador, es decir, storyboardID, exista en Identity Inspector para UIViewController):
UIStoryboard *mainStoryBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
NewRootViewController *newRootViewController = [mainStoryBoard instantiateViewControllerWithIdentifier:@"NewRootViewController"];
- b) programáticamente (podría agregar Subvista, etc.)
UIViewController *newRootViewController = [[UIViewController alloc] init];
newRootViewController.view = [[UIView alloc] initWithFrame:CGRectMake(0, 50, 320, 430)];
newRootViewController.view.backgroundColor = [UIColor whiteColor];
3) Poniéndolo todo junto ...
testAppDelegate.window.rootViewController = newRootViewController;
[testAppDelegate.window makeKeyAndVisible];
4) Incluso puedes lanzar una animación ...
testAppDelegate.window.rootViewController = newRootViewController;
[testAppDelegate.window makeKeyAndVisible];
newRootViewController.view.alpha = 0.0;
[UIView animateWithDuration:2.0 animations:^{
newRootViewController.view.alpha = 1.0;
}];
¡Espero que esto ayude a alguien! Aclamaciones.
El controlador de vista raíz para la ventana.
El controlador de vista raíz proporciona la vista de contenido de la ventana. Al asignar un controlador de vista a esta propiedad (ya sea programáticamente o mediante el Interface Builder) se instala la vista del controlador de vista como vista de contenido de la ventana. Si la ventana tiene una jerarquía de vistas existente, las vistas anteriores se eliminan antes de que se instalen las nuevas. El valor predeterminado de esta propiedad es nulo.
* Actualización 2/09/2015
Como se señala a continuación, debe manejar la eliminación del controlador de vista anterior cuando se presenta el nuevo controlador de vista. Puede elegir tener un controlador de vista de transición en el que manejará esto. Aquí hay algunos consejos sobre cómo implementar esto:
[UIView transitionWithView:self.containerView
duration:0.50
options:options
animations:^{
//Transition of the two views
[self.viewController.view removeFromSuperview];
[self.containerView addSubview:aViewController.view];
}
completion:^(BOOL finished){
//At completion set the new view controller.
self.viewController = aViewController;
}];
Es más habitual utilizar un "controlador de vista presentado" (presente presentViewController:animated:completion:
. Puede tener tantas como quiera, apareciendo efectivamente frente a (y básicamente reemplazando) el controlador de vista raíz. No tiene que haber ninguna animación si no quieres, o no puede haber. Puede descartar el controlador de vista presentado para volver al controlador de vista raíz original, pero no es necesario; el controlador de vista presentado puede estar allí para siempre si lo desea.
Aquí está la sección sobre los controladores de vista presentados de mi libro:
http://www.apeth.com/iOSBook/ch19.html#_presented_view_controller
En este diagrama (de antes en ese capítulo), un controlador de vista presentado se ha apoderado por completo de la interfaz de la aplicación; el controlador de vista raíz y sus subvistas ya no están en la interfaz. El controlador de vista raíz todavía existe, pero es liviano y no importa.