ios status - Intente presentar UIViewController en UIViewController cuya vista no se encuentra en la jerarquía de ventanas




bar ionic (23)

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ó.

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];

Mi problema era que estaba ejecutando el segue en el método didFinishLaunchingWithOptions antes de llamar a makeKeyAndVisible() en la ventana.


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.


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
        })
    }
}

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];
    ...
}

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 .


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

}

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

}

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.


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.


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.


Si tiene el objeto AVPlayer con el video reproducido, primero debe pausar el video.


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.


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:


¿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.


viewWillLayoutSubviews y viewDidLayoutSubviews (iOS 5.0+) se pueden usar para este propósito. Se llaman antes que viewDidAppear.


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.


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];

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 !!


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í.


EDIT: esta solución está en desuso en iOS 9. Elija una de las otras respuestas.

Con UIViewControllerBasedStatusBarAppearance establecido en NO, pude establecer el estilo en texto blanco usando:

[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackTranslucent;

Esto se debe a que el color del texto en este estilo era blanco en iOS 6 y versiones inferiores.

ACTUALIZACIÓN: según @jowie puedes probarlo en iOS8:

[UIApplication sharedApplication].statusBarStyle = UIBarStyleBlack;




ios cocoa-touch ios6 views hierarchy