ios - plugin - Intente presentar UIViewController en UIViewController cuya vista no se encuentra en la jerarquÃa de ventanas
status bar ios ionic (27)
¿De dónde llamas a este método? Tuve un problema en el que intentaba presentar un controlador de vista modal dentro del método viewDidLoad
. La solución para mí fue mover esta llamada al método viewDidAppear:
.
Mi presunción es que la vista del controlador de vista no está en la jerarquía de vista de la ventana en el punto en que se ha cargado (cuando se viewDidLoad
mensaje viewDidLoad
), pero está en la jerarquía de ventana después de que se haya presentado (cuando viewDidAppear:
mensaje viewDidAppear:
se ha enviado).
Precaución
Si realiza una llamada a presentViewController:animated:completion:
en viewDidAppear:
puede tener un problema por el cual el controlador de vista modal siempre se presenta cada vez que aparece la vista del controlador de vista (¡lo cual tiene sentido!) Y por lo tanto el controlador de vista modal ser presentado nunca desaparecerá ...
Quizás este no sea el mejor lugar para presentar el controlador de vista modal, o quizás deba mantenerse algún estado adicional que permita al controlador de vista actual decidir si debe presentar el controlador de vista modal de manera inmediata o no.
Acabo de comenzar a usar Xcode 4.5 y recibí este error en la consola:
Advertencia: ¡Intente presentar <finishViewController: 0x1e56e0a0> en <ViewController: 0x1ec3e000> cuya vista no está en la jerarquía de ventanas!
La vista aún se está presentando y todo en la aplicación funciona bien. ¿Es esto algo nuevo en iOS 6?
Este es el código que estoy usando para cambiar entre vistas:
UIStoryboard *storyboard = self.storyboard;
finishViewController *finished =
[storyboard instantiateViewControllerWithIdentifier:@"finishViewController"];
[self presentViewController:finished animated:NO completion:NULL];
Con su ventana principal, es probable que siempre haya momentos con transiciones que sean incompatibles con la presentación de una alerta. Para poder presentar alertas en cualquier momento del ciclo de vida de su aplicación, debe tener una ventana separada para realizar el trabajo.
/// independant window for alerts
@interface AlertWindow: UIWindow
+ (void)presentAlertWithTitle:(NSString *)title message:(NSString *)message;
@end
@implementation AlertWindow
+ (AlertWindow *)sharedInstance
{
static AlertWindow *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[AlertWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
});
return sharedInstance;
}
+ (void)presentAlertWithTitle:(NSString *)title message:(NSString *)message
{
// Using a separate window to solve "Warning: Attempt to present <UIAlertController> on <UIViewController> whose view is not in the window hierarchy!"
UIWindow *shared = AlertWindow.sharedInstance;
shared.userInteractionEnabled = YES;
UIViewController *root = shared.rootViewController;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
alert.modalInPopover = true;
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
shared.userInteractionEnabled = NO;
[root dismissViewControllerAnimated:YES completion:nil];
}]];
[root presentViewController:alert animated:YES completion:nil];
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
self.userInteractionEnabled = NO;
self.windowLevel = CGFLOAT_MAX;
self.backgroundColor = UIColor.clearColor;
self.hidden = NO;
self.rootViewController = UIViewController.new;
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(bringWindowToTop:)
name:UIWindowDidBecomeVisibleNotification
object:nil];
return self;
}
/// Bring AlertWindow to top when another window is being shown.
- (void)bringWindowToTop:(NSNotification *)notification {
if (![notification.object isKindOfClass:[AlertWindow class]]) {
self.hidden = YES;
self.hidden = NO;
}
}
@end
Uso básico que, por diseño, siempre tendrá éxito:
[AlertWindow presentAlertWithTitle:@"My title" message:@"My message"];
En caso de que ayude a alguien, mi problema era extremadamente tonto. Totalmente mi culpa, por supuesto. Una notificación estaba activando un método que estaba llamando al modal. Pero no estaba eliminando la notificación correctamente, por lo que en algún momento tendría más de una notificación, por lo que se llamaría al modal varias veces. Por supuesto, después de llamar al modal una vez, el controlador de vista que lo llama ya no está en la jerarquía de vista, por eso vemos este problema. Mi situación causó un montón de otros problemas también, como es de esperar.
Entonces, para resumir, haga lo que haga, asegúrese de que el modal no se llame más de una vez .
En mi situación, no pude poner el mío en una anulación de clase. Entonces, aquí está lo que tengo:
let viewController = self // I had viewController passed in as a function,
// but otherwise you can do this
// Present the view controller
let currentViewController = UIApplication.shared.keyWindow?.rootViewController
currentViewController?.dismiss(animated: true, completion: nil)
if viewController.presentedViewController == nil {
currentViewController?.present(alert, animated: true, completion: nil)
} else {
viewController.present(alert, animated: true, completion: nil)
}
Está funcionando bien, intenta esto. Link
UIViewController *top = [UIApplication sharedApplication].keyWindow.rootViewController;
[top presentViewController:secondView animated:YES completion: nil];
Este tipo de advertencia puede significar que está intentando presentar un nuevo View Controller
través del Navigation Controller
mientras este Navigation Controller
presenta actualmente otro View Controller
. Para solucionarlo, debe descartar el View Controller
presentado al principio y, al finalizar, presentar el nuevo. Otra causa de la advertencia puede ser intentar presentar View Controller
en un hilo diferente al main
.
He intentado muchas respuestas, pero nadie me ayuda. La única solución que me funciona es la siguiente. Solución 4 <ViewController: 0x1ec3e000> ¡cuya vista no se encuentra en la jerarquía de ventanas!
override func viewWillAppear(_ animated: Bool) {
let appDelegate = UIApplication.shared.delegate as? AppDelegate
appDelegate?.window?.rootViewController = self
}
Lamentablemente, la solución aceptada no funcionó para mi caso. Estaba tratando de navegar a un nuevo View Controller justo después de desconectarme de otro View Controller.
Encontré una solución mediante el uso de una marca para indicar a qué se ha llamado Segue de desbobinado.
@IBAction func unwindFromAuthenticationWithSegue(segue: UIStoryboardSegue) {
self.shouldSegueToMainTabBar = true
}
@IBAction func unwindFromForgetPasswordWithSegue(segue: UIStoryboardSegue) {
self.shouldSegueToLogin = true
}
Luego presente el VC deseado con el present(_ viewControllerToPresent: UIViewController)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if self.shouldSegueToMainTabBar {
let mainTabBarController = storyboard.instantiateViewController(withIdentifier: "mainTabBarVC") as! MainTabBarController
self.present(mainTabBarController, animated: true)
self.shouldSegueToMainTabBar = false
}
if self.shouldSegueToLogin {
let loginController = storyboard.instantiateViewController(withIdentifier: "loginVC") as! LogInViewController
self.present(loginController, animated: true)
self.shouldSegueToLogin = false
}
}
Básicamente, el código anterior me permitirá detectar el desenlace del inicio de sesión / SignUp VC y navegar al panel de control, o detectar la acción de desenrollar desde la contraseña VC y navegar a la página de inicio de sesión.
Lo arreglé moviendo la función de start()
dentro del bloque de finalización de dismiss
:
self.tabBarController.dismiss(animated: false) {
self.start()
}
Start contiene dos llamadas a self.present()
una para un UINavigationController y otra para un UIImagePickerController
.
Eso lo arregló para mí.
Me ocurrió que la secuencia en el guión gráfico estaba rota . Eliminar el segue (y crear el mismo segue de nuevo) resolvió el problema.
Mi problema era que estaba ejecutando el segue en el método didFinishLaunchingWithOptions
antes de llamar a makeKeyAndVisible()
en la ventana.
Otra causa potencial:
Tuve este problema cuando accidentalmente presentaba el mismo controlador de vista dos veces. (Una vez con performSegueWithIdentifer:sender:
que se llamó cuando se presionó el botón, y una segunda vez con un segmento conectado directamente al botón).
Efectivamente, se dispararon dos segmentos al mismo tiempo, y obtuve el error: ¡ Attempt to present X on Y whose view is not in the window hierarchy!
Para mostrar cualquier subvista a la vista principal, utilice el siguiente código
UIViewController *yourCurrentViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (yourCurrentViewController.presentedViewController)
{
yourCurrentViewController = yourCurrentViewController.presentedViewController;
}
[yourCurrentViewController presentViewController:composeViewController animated:YES completion:nil];
Para descartar cualquier subvista desde la vista principal, utilice el siguiente código
UIViewController *yourCurrentViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (yourCurrentViewController.presentedViewController)
{
yourCurrentViewController = yourCurrentViewController.presentedViewController;
}
[yourCurrentViewController dismissViewControllerAnimated:YES completion:nil];
Probablemente, como yo, tienes una raíz incorrecta viewController
Quiero mostrar un ViewController
en un contexto que non-UIViewController
,
Así que no puedo usar tal código:
[self presentViewController:]
Entonces, me sale un UIViewController:
[[[[UIApplication sharedApplication] delegate] window] rootViewController]
Por alguna razón (error lógico), el rootViewController
es diferente de lo esperado (un UIViewController
normal). Luego corrijo el error, reemplazando rootViewController
con un UINavigationController
, y el problema desapareció.
Si otras soluciones no se ven bien por alguna razón, aún puede usar esta buena workaround
para presentar con la demora de 0, como esto:
dispatch_after(0, dispatch_get_main_queue(), ^{
finishViewController *finished = [self.storyboard instantiateViewControllerWithIdentifier:@"finishViewController"];
[self presentViewController:finished animated:NO completion:NULL];
});
Si bien no he visto ninguna garantía documentada de que su VC esté en la jerarquía de vistas en el bloque de despacho de tiempo programado para la ejecución, he observado que funcionaría bien.
El uso de un retraso de, por ejemplo, 0,2 segundos también es una opción Y lo mejor: de esta manera, no es necesario meterse con la variable booleana en viewDidAppear:
Si tiene el objeto AVPlayer con el video reproducido, primero debe pausar el video.
También he encontrado este problema cuando intenté presentar un controlador de vista en viewDidLoad
. He intentado la respuesta de James Bedford. Funciona, pero mi aplicación mostrará el fondo en primer lugar durante 1 o 2 segundos.
Después de la investigación, finalmente encontré otra manera de resolver esto es usar el controlador de vista infantil.
- (void)viewDidLoad
{
...
[self.view addSubview:navigationViewController.view];
[self addChildViewController:navigationViewController];
...
}
También puede obtener esta advertencia al realizar un segmento desde un controlador de vista que está incrustado en un contenedor. La solución correcta es utilizar segue del elemento primario del contenedor, no del controlador de vista del contenedor.
También tuve este problema, pero no tuvo nada que ver con el momento. Estaba usando un singleton para manejar las escenas, y lo puse como el presentador. En otras palabras, "Yo" no estaba conectado a nada. Acabo de hacer su "escena" interior el nuevo presentador y listo, funcionó. (Voila pierde su toque después de que aprendas su significado, jeje).
Entonces, sí, no se trata de "encontrar mágicamente el camino correcto", se trata de entender dónde se encuentra su código y qué está haciendo. Estoy feliz de que Apple haya dado un mensaje de advertencia tan simple en inglés, incluso con emoción. Felicitaciones a la manzana dev que hizo eso !!
Tengo que escribir debajo de la línea.
self.searchController.definesPresentationContext = true
en lugar de
self.definesPresentationContext = true
en UIViewController
Terminé con un código de este tipo que finalmente me funciona (Swift), teniendo en cuenta que desea mostrar algún viewController
de viewController
desde prácticamente cualquier lugar. Este código obviamente se bloqueará cuando no haya un controlador de control de raíz disponible, ese es el final abierto. Tampoco incluye el cambio que generalmente se requiere para el hilo de la interfaz de usuario usando
dispatch_sync(dispatch_get_main_queue(), {
guard !NSBundle.mainBundle().bundlePath.hasSuffix(".appex") else {
return; // skip operation when embedded to App Extension
}
if let delegate = UIApplication.sharedApplication().delegate {
delegate.window!!.rootViewController?.presentViewController(viewController, animated: true, completion: { () -> Void in
// optional completion code
})
}
}
Tuve el mismo problema. El problema era que el performSegueWithIdentifier fue activado por una notificación, tan pronto como puse la notificación en el hilo principal, el mensaje de advertencia había desaparecido.
Tuve este problema y la causa raíz se suscribía a un controlador de clic de botón (TouchUpInside) varias veces.
Se suscribió en ViewWillAppear, que se llamaba varias veces desde que habíamos agregado la navegación para ir a otro controlador y luego volver a desconectarlo.
Yo tuve el mismo problema. Tuve que incrustar un controlador de navegación y presentar el controlador a través de él. A continuación se muestra el código de ejemplo.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIImagePickerController *cameraView = [[UIImagePickerController alloc]init];
[cameraView setSourceType:UIImagePickerControllerSourceTypeCamera];
[cameraView setShowsCameraControls:NO];
UIView *cameraOverlay = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 768, 1024)];
UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"someImage"]];
[imageView setFrame:CGRectMake(0, 0, 768, 1024)];
[cameraOverlay addSubview:imageView];
[cameraView setCameraOverlayView:imageView];
[self.navigationController presentViewController:cameraView animated:NO completion:nil];
// [self presentViewController:cameraView animated:NO completion:nil]; //this will cause view is not in the window hierarchy error
}
viewWillLayoutSubviews
y viewDidLayoutSubviews
(iOS 5.0+) se pueden usar para este propósito. Se llaman antes que viewDidAppear.
Con Swift 3 ...
Otra posible causa de esto, que me sucedió, fue tener un segue de una tableViewCell a otro ViewController en el Storyboard. También utilicé override func prepare(for segue: UIStoryboardSegue, sender: Any?) {}
Cuando se hizo clic en la celda.
Solucioné este problema haciendo un segmento de ViewController a ViewController.
TL; DR. Solo puede tener 1 rootViewController y es el más reciente. Así que no intente que un controlador de vista presente otro controlador de vista cuando ya se haya presentado uno que no se haya descartado.
Después de hacer algunas de mis propias pruebas, he llegado a una conclusión.
Si tiene un rootViewController que desea presentar todo, entonces puede tener este problema.
Aquí está mi código de rootController (abrir es mi acceso directo para presentar un controlador de vista desde la raíz).
func open(controller:UIViewController)
{
if (Context.ROOTWINDOW.rootViewController == nil)
{
Context.ROOTWINDOW.rootViewController = ROOT_VIEW_CONTROLLER
Context.ROOTWINDOW.makeKeyAndVisible()
}
ROOT_VIEW_CONTROLLER.presentViewController(controller, animated: true, completion: {})
}
Si llamo abierto dos veces seguidas (independientemente del tiempo transcurrido), esto funcionará bien en la primera apertura, pero NO en la segunda apertura. El segundo intento de apertura dará lugar al error anterior.
Sin embargo, si cierro la vista que se presentó más recientemente y luego la llamada abierta, funciona bien cuando vuelvo a llamar abierta (en otro controlador de vista).
func close(controller:UIViewController)
{
ROOT_VIEW_CONTROLLER.dismissViewControllerAnimated(true, completion: nil)
}
Lo que he concluido es que el controlador de control de raíz de solo MOST-RECENT-CALL se encuentra en la jerarquía de la vista (incluso si no la descartó o eliminó una vista). Intenté jugar con todas las llamadas del cargador (viewDidLoad, viewDidAppear, y haciendo llamadas de despacho retrasadas) y he encontrado que la única manera de que SOLAMENTE funcione es llamando al presente desde el controlador de vista más superior.