ios - life - view controller xcode




Tentativo di presentare UIViewController su UIViewController la cui vista non è nella gerarchia delle finestre (18)

È inoltre possibile ottenere questo avviso quando si esegue un passaggio da un controller di visualizzazione incorporato in un contenitore. La soluzione corretta consiste nell'utilizzare segue dal padre del contenitore, non dal controller di visualizzazione del contenitore.

Ho appena iniziato ad usare Xcode 4.5 e ho ricevuto questo errore nella console:

Avvertenza: tentare di presentare <finishViewController: 0x1e56e0a0> su <ViewController: 0x1ec3e000> la cui vista non è nella gerarchia della finestra!

La vista è ancora in fase di presentazione e tutto nell'app funziona perfettamente. È qualcosa di nuovo in iOS 6?

Questo è il codice che sto usando per cambiare tra le visualizzazioni:

UIStoryboard *storyboard = self.storyboard;
finishViewController *finished = 
[storyboard instantiateViewControllerWithIdentifier:@"finishViewController"];

[self presentViewController:finished animated:NO completion:NULL];

Con la tua finestra principale, ci saranno probabilmente sempre orari con transizioni incompatibili con la presentazione di un avviso. Per consentire la presentazione di avvisi in qualsiasi momento nel ciclo di vita dell'applicazione, è necessario disporre di una finestra separata per eseguire il lavoro.

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

Utilizzo di base che, in base alla progettazione, avrà sempre successo:

[AlertWindow presentAlertWithTitle:@"My title" message:@"My message"];

Devo scrivere sotto la linea.

self.searchController.definesPresentationContext = true

invece di

self.definesPresentationContext = true

in UIViewController


Funziona bene, prova questo. Link

UIViewController *top = [UIApplication sharedApplication].keyWindow.rootViewController;
[top presentViewController:secondView animated:YES completion: nil];

Ho appena avuto questo problema, ma non aveva niente a che fare con i tempi. Stavo usando un singleton per gestire le scene e l'ho impostato come presentatore. In altre parole, il "Sé" non era collegato a nulla. Ho appena fatto la sua "scena" interiore il nuovo presentatore e voilà, ha funzionato. (Voila perde il suo tocco dopo aver appreso il suo significato, heh).

Quindi sì, non si tratta di "trovare magicamente la strada giusta", si tratta di capire dove si trova il tuo codice e cosa sta facendo. Sono felice che Apple abbia dato un messaggio di avvertimento semplice in inglese, anche con emozione. Complimenti al dev della mela che l'ha fatto !!


Ho avuto lo stesso problema. Ho dovuto incorporare un controller di navigazione e presentare il controller attraverso di esso. Di seguito è riportato il codice di esempio.

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

}

Ho finito con un tale codice che finalmente funziona con me (Swift), considerando che vuoi visualizzare qualche viewController praticamente da qualsiasi luogo. Ovviamente questo codice andrà in crash quando non ci sono rootViewController disponibili, ovvero il finale aperto. Inoltre, non include solitamente il passaggio all'interfaccia utente 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
        })
    }
}

Ho provato molte risposte ma nessuno mi aiuta, l'unica soluzione che funziona per me è muggente. 4 Soluzione <ViewController: 0x1ec3e000> la cui vista non è nella gerarchia delle finestre!

    override func viewWillAppear(_ animated: Bool) {
    let appDelegate = UIApplication.shared.delegate as? AppDelegate
  appDelegate?.window?.rootViewController  = self

}

Il mio problema era che stavo eseguendo il seguito nel metodo di didFinishLaunchingWithOptions prima di chiamare makeKeyAndVisible() sulla finestra.


L'ho risolto spostando la funzione start() all'interno del blocco di completamento di eliminazione:

self.tabBarController.dismiss(animated: false) {
  self.start()
}

Start contiene due chiamate a self.present() uno per un UINavigationController e un altro per un UIImagePickerController .

Questo lo ha risolto per me.


Nel caso in cui aiuti qualcuno, il mio problema è stato estremamente sciocco. Totalmente colpa mia, naturalmente. Una notifica stava attivando un metodo che stava chiamando il modal. Ma non stavo rimuovendo la notifica correttamente, quindi ad un certo punto avrei avuto più di una notifica, quindi il modale sarebbe stato chiamato più volte. Ovviamente, dopo aver chiamato la modale una volta, il viewcontroller che la chiama non è più nella gerarchia della vista, ecco perché vediamo questo problema. La mia situazione ha causato anche una serie di altri problemi, come ci si aspetterebbe.

Quindi, per riassumere, qualunque cosa tu stia facendo assicurati che la modale non venga chiamata più di una volta .


Nella mia situazione, non ero in grado di mettere il mio in un override di classe. Quindi, ecco cosa ho ottenuto:

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

Probabilmente, come me, hai una vista di root viewController

Voglio visualizzare un ViewController in un contesto non-UIViewController ,

Quindi non posso usare questo codice:

[self presentViewController:]

Quindi, ottengo un UIViewController:

[[[[UIApplication sharedApplication] delegate] window] rootViewController]

Per qualche ragione (bug logico), rootViewController è qualcosa di diverso dal previsto (un normale UIViewController ). Quindi correggo il bug, sostituendo rootViewController con un UINavigationController , e il problema è scomparso.


Purtroppo, la soluzione accettata non ha funzionato per il mio caso. Stavo cercando di passare a un nuovo controller di visualizzazione subito dopo il distacco da un altro controller di visualizzazione.

Ho trovato una soluzione utilizzando un flag per indicare quale percorso di svolgimento è stato chiamato.

@IBAction func unwindFromAuthenticationWithSegue(segue: UIStoryboardSegue) {
    self.shouldSegueToMainTabBar = true
}

@IBAction func unwindFromForgetPasswordWithSegue(segue: UIStoryboardSegue) {
    self.shouldSegueToLogin = true
}

Quindi presentare il VC desiderato con 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
    }
}

Fondamentalmente, il codice di cui sopra mi consentirà di cogliere la pausa dal login / registrazione VC e navigare verso la dashboard, o prendere l'azione di svolgimento da dimenticare la password VC e navigare fino alla pagina di accesso.


Se hai un oggetto AVPlayer con un video riprodotto, devi prima mettere in pausa il video.


Se per qualche motivo le altre soluzioni non sembrano buone, puoi ancora usare questa buona vecchia workaround per presentare il ritardo di 0, in questo modo:

dispatch_after(0, dispatch_get_main_queue(), ^{
    finishViewController *finished = [self.storyboard instantiateViewControllerWithIdentifier:@"finishViewController"];
    [self presentViewController:finished animated:NO completion:NULL];    
});

Mentre non ho visto alcuna garanzia documentata che il tuo VC sarebbe nella gerarchia della vista sul blocco di tempo in cui è programmato l'esecuzione, ho notato che funzionerebbe perfettamente.

Anche l'utilizzo del ritardo di 0,2 secondi è un'opzione. E la cosa migliore: in questo modo non è necessario modificare la variabile booleana in viewDidAppear:


viewWillLayoutSubviews e viewDidLayoutSubviews (iOS 5.0+) possono essere utilizzati a questo scopo. Sono chiamati prima di viewDidAppear.


Con Swift 3 ...

Un'altra possibile causa di questo, che mi è successo, era il passaggio da un tavoloViewCell ad un altro ViewController sullo Storyboard. Ho anche usato override func prepare(for segue: UIStoryboardSegue, sender: Any?) {} Quando si fa clic sulla cella.

Ho risolto questo problema effettuando un passaggio da ViewController a ViewController.





hierarchy