it-swarm.com.de

Versuchen Sie, UIViewController auf UIViewController zu präsentieren, dessen Ansicht nicht in der Fensterhierarchie liegt

Ich habe gerade mit Xcode 4.5 angefangen und diese Fehlermeldung wurde in der Konsole angezeigt:

Warnung: Versuchen Sie, <finishViewController: 0x1e56e0a0> auf <ViewController: 0x1ec3e000> darzustellen, dessen Ansicht nicht in der Fensterhierarchie ist!

Die Ansicht wird immer noch präsentiert und alles in der App funktioniert einwandfrei. Ist das etwas Neues in iOS 6?

Dies ist der Code, den ich verwende, um zwischen den Ansichten zu wechseln:

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

[self presentViewController:finished animated:NO completion:NULL];
525
Kyle Goslan

Woher nennst du diese Methode? Ich hatte ein Problem, bei dem ich versuchte, einen Controller für modale Ansichten innerhalb der viewDidLoad-Methode zu präsentieren. Die Lösung für mich bestand darin, diesen Aufruf auf die viewDidAppear:-Methode zu verschieben.

Meine Vermutung ist, dass die Ansichtdes View-Controllers an der Stelle, an der er geladen wurde (wenn die viewDidLoad-Nachricht gesendet wird) nichtin der Ansichtshierarchie des Fensters ist, sondernistin der Fensterhierarchie, nachdem sie präsentiert wurde (wenn die viewDidAppear:-Nachricht gesendet wird).


Vorsicht

Wenn Sie presentViewController:animated:completion: im viewDidAppear: aufrufen, kann es zu einem Problem kommen, bei dem der modale Ansichtscontroller immer dann angezeigt wird, wenn die Ansicht des Ansichtscontrollers angezeigt wird (was sinnvoll ist!). Der modale Ansichtscontroller wird daher nicht gelöscht ...

Möglicherweise ist dies nicht der beste Ort, um den Modal-View-Controller zu präsentieren, oder es muss möglicherweise ein zusätzlicher Status beibehalten werden, sodass der Präsentations-View-Controller entscheiden kann, ob er den Modal-View-Controller sofort anzeigen soll oder nicht.

1136
James Bedford

Eine andere mögliche Ursache:

Ich hatte dieses Problem, als ich versehentlich zweimal denselben View-Controller präsentierte. (Einmal mit performSegueWithIdentifer:sender:, das beim Drücken der Taste aufgerufen wurde, und ein zweites Mal, wenn ein Segment direkt mit der Schaltfläche verbunden ist).

Tatsächlich feuerten zwei Segmente gleichzeitig, und ich erhielt den Fehler: Attempt to present X on Y whose view is not in the window hierarchy!

57
Chris Nolet

Zu diesem Zweck können viewWillLayoutSubviews und viewDidLayoutSubviews (iOS 5.0+) verwendet werden. Sie werden früher als viewDidAppear aufgerufen.

34
Jonny

Für die Anzeige einer Unteransicht zur Hauptansicht verwenden Sie bitte folgenden Code

UIViewController *yourCurrentViewController = [UIApplication sharedApplication].keyWindow.rootViewController;

while (yourCurrentViewController.presentedViewController) 
{
   yourCurrentViewController = yourCurrentViewController.presentedViewController;
}

[yourCurrentViewController presentViewController:composeViewController animated:YES completion:nil];

Für das Schließen einer Unteransicht aus der Hauptansicht verwenden Sie bitte folgenden Code

UIViewController *yourCurrentViewController = [UIApplication sharedApplication].keyWindow.rootViewController;

while (yourCurrentViewController.presentedViewController) 
{
   yourCurrentViewController = yourCurrentViewController.presentedViewController;
}

[yourCurrentViewController dismissViewControllerAnimated:YES completion:nil];
21
abdul sathar

Dieses Problem ist auch aufgetreten, als ich versuchte, einen Ansichtscontroller in viewDidLoad darzustellen. Ich habe James Bedfords Antwort ausprobiert. Es funktioniert, aber meine App zeigt den Hintergrund zuerst für 1 oder 2 Sekunden an. 

Nach Recherchen finde ich schließlich einen anderen Weg, dies zu lösen, ist die Verwendung eines untergeordneten Ansichtscontrollers.

- (void)viewDidLoad
{
    ...
    [self.view addSubview:navigationViewController.view];
    [self addChildViewController:navigationViewController];
    ...
}
16
sunkehappy

Wahrscheinlich haben Sie wie ich eine falsche Wurzel viewController

Ich möchte eine ViewController in einem non-UIViewController-Kontext anzeigen,

Also kann ich solchen Code nicht verwenden:

[self presentViewController:]

Ich bekomme also einen UIViewController:

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

Aus irgendeinem Grund (logischer Fehler) ist die rootViewController etwas anderes als erwartet (eine normale UIViewController). Dann korrigiere ich den Fehler, ersetze rootViewController durch eine UINavigationController und das Problem ist weg.

13
Sanbrother

TL; DR Sie können nur einen rootViewController und den zuletzt präsentierten haben. Versuchen Sie also nicht, dass ein Viewcontroller einen anderen Viewcontroller präsentiert, der bereits einen präsentiert hat, der noch nicht abgelehnt wurde.

Nach einigen eigenen Tests bin ich zu einem Ergebnis gekommen.

Wenn Sie über einen rootViewController verfügen, der alles darstellen soll, können Sie auf dieses Problem stoßen.

Hier ist mein rootController-Code (open ist meine Abkürzung für die Darstellung eines Viewcontrollers aus dem Stammverzeichnis).

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

Wenn ich zweimal hintereinander offen rufe (unabhängig von der verstrichenen Zeit), wird dies beim ersten Öffnen gut funktionieren, beim zweiten Öffnen jedoch NICHT. Der zweite Öffnungsversuch führt zu dem obigen Fehler. 

Wenn ich jedoch die zuletzt präsentierte Ansicht schließe und aufrufe, klappt es ganz gut, wenn ich wieder aufrufe (auf einem anderen Ansichtscontroller).

func close(controller:UIViewController)
{
    ROOT_VIEW_CONTROLLER.dismissViewControllerAnimated(true, completion: nil)
}

Ich habe daraus geschlossen, dass der rootViewController nur des MOST-RECENT-CALL in der Ansichtshierarchie ist (auch wenn Sie ihn nicht abgewiesen oder eine Ansicht entfernt haben). Ich habe versucht, mit all den Loader-Aufrufen (viewDidLoad, viewDidAppear und verzögerten Dispatch-Aufrufen) zu spielen, und ich habe festgestellt, dass die einzige Möglichkeit, wie ich es zum Laufen bringen kann, NUR das Aufrufen von Präsens des obersten View Controllers ist. 

5
Aggressor

Mein Problem war, dass ich das Segment in UIApplicationDelegates didFinishLaunchingWithOptions-Methode durchführte, bevor ich makeKeyAndVisible() im Fenster aufrief.

5
Adam Johns

Ich bin mit einem solchen Code gelandet, der endlich für mich funktioniert (Swift), wenn man bedenkt, dass man viewController von praktisch überall anzeigen möchte. Dieser Code wird offensichtlich abstürzen, wenn kein rootViewController verfügbar ist. Dies ist die offene Endung. Es beinhaltet auch nicht die normalerweise erforderliche Umstellung auf den UI-Thread 

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

Wenn Sie ein AVPlayer-Objekt mit abgespieltem Video haben, müssen Sie das Video zuerst anhalten.

3
Vlad

Es funktioniert gut, probiere es aus. Link

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

Ich hatte das gleiche Problem. Das Problem war, der performSegueWithIdentifier wurde durch eine Benachrichtigung ausgelöst, sobald ich die Benachrichtigung im Haupt-Thread ablegte, war die Warnmeldung weg. 

3
Red

Ich hatte das gleiche Problem. Ich musste einen Navigationscontroller einbetten und den Controller durch ihn präsentieren. Unten ist der Beispielcode.

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

}
2
resting

Falls es jemandem hilft, war mein Thema extrem dumm. Total meine Schuld natürlich. Eine Benachrichtigung löste eine Methode aus, die das Modal aufrief. Aber ich habe die Benachrichtigung nicht korrekt entfernt, also hätte ich irgendwann mehr als eine Benachrichtigung, so dass der Modal mehrmals aufgerufen wurde. Nachdem Sie den Modal einmal aufgerufen haben, ist der Viewcontroller, der ihn ruft, natürlich nicht mehr in der Ansichtshierarchie. Deshalb sehen wir dieses Problem. Meine Situation verursachte auch eine Reihe anderer Probleme, wie man es erwarten würde.

Um zusammenzufassen, was auch immer Sie tun, stellen Sie sicher, dass das Modal nicht mehr als einmal aufgerufen wird .

2
HotFudgeSunday

In meiner Situation konnte ich meine Klasse nicht in eine Klassenumschaltung setzen. Also hier ist was ich habe:

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

Es ist mir passiert, dass der Bereich im Storyboard irgendwie gebrochen war . Durch Löschen des Segments (und erneutes Erstellen des gleichen Segues) wurde das Problem behoben.

1
vonox7

Muß unter die Zeile schreiben.

self.searchController.definesPresentationContext = true

anstatt 

self.definesPresentationContext = true

in UIViewController 

1
Coder_A_D

Sie können diese Warnung auch erhalten, wenn Sie ein Segment von einem in einen Container eingebetteten View Controller ausführen. Die richtige Lösung ist die Verwendung von segue vom übergeordneten Container, nicht vom View-Controller des Containers.

1
Borzh

In Ihrem Hauptfenster gibt es wahrscheinlich immer Zeiten mit Übergängen, die mit der Anzeige einer Warnung nicht kompatibel sind. Um die Anzeige von Warnmeldungen jederzeit in Ihrem Anwendungslebenszyklus zu ermöglichen, sollten Sie über ein separates Fenster verfügen, um die Aufgabe auszuführen.

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

Grundlegende Verwendung, die von Entwurf immer erfolgreich sein wird:

[AlertWindow presentAlertWithTitle:@"My title" message:@"My message"];
1
Cœur

Mit Swift 3 ...

Eine andere mögliche Ursache, die mir passierte, war ein Wechsel von tableViewCell zu einem anderen ViewController auf dem Storyboard. Ich habe auch override func prepare(for segue: UIStoryboardSegue, sender: Any?) {} verwendet, als die Zelle angeklickt wurde.

Ich habe dieses Problem behoben, indem ich von ViewController zu ViewController übergegangen bin.

1
Devbot10

Ich hatte dieses Problem, und die Hauptursache war das mehrfache Abonnieren eines Button-Click-Handlers (TouchUpInside).

Es wurde in ViewWillAppear abonniert, das mehrmals aufgerufen wurde, seit wir die Navigation hinzugefügt hatten, um zu einem anderen Controller zu navigieren und sich danach wieder zu entspannen.

1
Wes

Ich hatte auch gerade dieses Problem, aber es hatte nichts mit dem Timing zu tun. Ich habe ein Singleton verwendet, um mit Szenen umzugehen, und ich habe es als Moderator eingestellt. Mit anderen Worten, "Selbst" war an nichts gebunden. Ich habe gerade seine innere "Szene" zum neuen Moderator gemacht und voila, es hat funktioniert. (Voila verliert seine Berührung, nachdem Sie seine Bedeutung gelernt haben, heh).

Es geht also nicht darum, "magisch den richtigen Weg zu finden", sondern um zu verstehen, wo Ihr Code steht und was er tut. Ich bin froh, dass Apple eine so einfache englische Warnmeldung gegeben hat, auch wenn er mit Emotionen zu tun hat. Ein dickes Lob an den Apple-Entwickler, der das getan hat !!

0
Stephen J

Ich habe es behoben, indem ich die Funktion start() innerhalb des Vervollständigungsblocks dismiss verschoben habe:

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

Start enthält zwei Aufrufe an self.present(), einen für einen UINavigationController und einen weiteren für eine UIImagePickerController.

Das hat es für mich behoben.

0
Alper

Wenn andere Lösungen aus irgendeinem Grund nicht gut aussehen, können Sie immer noch dieses gute alte workaround des Präsentierens mit der Verzögerung von 0 verwenden, wie folgt:

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

Ich habe zwar keine dokumentierte Garantie dafür erhalten, dass sich Ihre VC in der Ansichtshierarchie zu dem Zeitpunkt befindet, zu dem der Dispatch-Block zur Ausführung geplant ist, aber ich habe festgestellt, dass dies gut funktionieren würde. 

Verwenden einer Verzögerung von z. 0,2 sec ist auch eine Option. Und das Beste - auf diese Weise brauchen Sie sich nicht mit booleschen Variablen in viewDidAppear: zu beschäftigen.

0
Dannie P

Leider hat die akzeptierte Lösung in meinem Fall nicht funktioniert. Ich habe versucht, direkt nach dem Abwickeln von einem anderen View Controller zu einem neuen View Controller zu navigieren.

Ich habe eine Lösung gefunden, indem ich mit einer Flagge angezeigt habe, welches Abwickelsegment aufgerufen wurde.

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

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

Dann präsentieren Sie die gewünschte VC mit 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
    }
}

Im Grunde erlaubt mir der obige Code, die Abwicklung von login/SignUp VC zu beobachten und zum Dashboard zu navigieren, oder die Abrollaktion über das Passwort vergessen VC und die Login-Seite.

0
Fan Jin

Diese Art von Warnung kann bedeuten, dass Sie versuchen, neuen View Controller durch Navigation Controller zu präsentieren, während dieser Navigation Controller derzeit einen anderen View Controller präsentiert. Um das Problem zu beheben, müssen Sie den aktuell präsentierten View Controller zunächst abweisen und nach Abschluss des Vorgangs den neuen ..__ angeben. Eine andere Ursache für die Warnung kann der Versuch sein, View Controller in einem anderen Thread als main darzustellen.

0
saltwat5r