Ostrzeżenie: próba prezentacji * na *, której widok nie znajduje się w hierarchii okien - szybko


83

Próbuję przedstawić, ViewController czy w modelu danych są jakieś zapisane dane. Ale pojawia się następujący błąd:

Ostrzeżenie: próba prezentacji * na *, którego widok nie znajduje się w hierarchii okien "

Odpowiedni kod:

override func viewDidLoad() {
    super.viewDidLoad()
    loginButton.backgroundColor = UIColor.orangeColor()

    var request = NSFetchRequest(entityName: "UserData")
    request.returnsObjectsAsFaults = false

    var appDel:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
    var context:NSManagedObjectContext = appDel.managedObjectContext!

    var results:NSArray = context.executeFetchRequest(request, error: nil)!

    if(results.count <= 0){
        print("Inga resultat")
    } else {
        print("SWITCH VIEW PLOX")
        let internVC = self.storyboard?.instantiateViewControllerWithIdentifier("internVC") as internViewController
        self.presentViewController(internVC, animated: true, completion: nil)
    }
}

Próbowałem różnych rozwiązań znalezionych przy użyciu Google bez powodzenia.


Czy podłączyłeś ViewController w Storyboard? Używasz tutaj NavigationController?
derdida,

Co masz na myśli, jeśli podłączyłem mój ViewControllerw Storyboard? Nie używam NavigationController@derdida
Nils Wasell

Wypróbuj: self.addChildViewController (childController: UIViewController) w ViewDidLoad
derdida

To generuje błąd. Oczekuje nazwy elementu członkowskiego lub wywołania konstruktora po „nazwie typu” ( UIViewControllerczęść). @derdida
Nils Wasell

Gdzie to napisałeś? W ViewDidLoad Twojego MainViewController?
derdida

Odpowiedzi:


127

W tym momencie w kodzie widok kontrolera widoku został tylko utworzony, ale nie został dodany do żadnej hierarchii widoków. Jeśli chcesz zaprezentować kontroler z tego widoku tak szybko, jak to możliwe, powinieneś to zrobić, viewDidAppearaby być najbezpieczniejszym.


2
Jeśli umieszczę go w viewDidAppear, to po odrzuceniu presentViewController ponownie wywoła metodę viewDidAppear i ponownie przedstawi viewController. Mam obejście polegające na umieszczeniu instrukcji warunkowej przed jej przedstawieniem, ale czy jest jakikolwiek sposób zamiast wstawić instrukcja warunkowa?
Puneet

2
Powiedziałbym, że próba przedstawienia innego modalu w momencie pojawienia się ekranu jest dziwnym zachowaniem, więc stan bool nie jest najgorszym rozwiązaniem. W tym samym czasie zastanów się nad głównym powodem, dla którego musisz wyświetlić modal i sprawdź, czy możesz uzyskać dostęp do tych informacji.
Acey

2
Jeśli zrobisz to w viewWillAppear, będziesz miał sytuację, w której jest w trakcie prezentacji, kiedy zaczynasz prezentację. Alternatywą byłoby umieszczenie go w viewWillAppear, ale użycie właściwości transitCoordinator nowej w systemie iOS 8 w celu dodania kodu w celu zakończenia przejścia. Więcej informacji można znaleźć tutaj developer.apple.com/library/prerelease/ios/documentation/UIKit/…
Acey

36

W celu c: To rozwiązało mój problem podczas prezentowania kontrolera widoku na mpmovieplayer

- (UIViewController*) topMostController
{
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;

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

    return topController;
}

34

Szybki 3

Miałem to ciągle pojawiać się jako nowicjusz i odkryłem, że obecne ładuje widoki modalne, które można odrzucić, ale przełączenie na kontroler główny jest najlepsze, jeśli nie musisz pokazywać modalu.

Używałem tego

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc  = storyboard?.instantiateViewController(withIdentifier: "MainAppStoryboard") as! TabbarController
present(vc, animated: false, completion: nil)

Używając tego zamiast tego z moim tabController:

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let view = storyboard.instantiateViewController(withIdentifier: "MainAppStoryboard") as UIViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
//show window
appDelegate.window?.rootViewController = view

Po prostu dostosuj się do kontrolera widoku, jeśli chcesz przełączać się między wieloma ekranami scenorysu.


16

Szybki 3.

Wywołaj tę funkcję, aby uzyskać najwyższy kontroler widoku, a następnie mieć obecny kontroler widoku.

func topMostController() -> UIViewController {
    var topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController!
        while (topController.presentedViewController != nil) {
            topController = topController.presentedViewController!
        }
        return topController
    }

Stosowanie:

let topVC = topMostController()
let vcToPresent = self.storyboard!.instantiateViewController(withIdentifier: "YourVCStoryboardID") as! YourViewController
topVC.present(vcToPresent, animated: true, completion: nil)

1
Dzięki @Jacob Davis za to! Użyłem go do przedstawienia kontrolera alertów, ponieważ otrzymałem Ostrzeżenie: próba przedstawienia ....
Gary Mansted

To jest bardzo przydatne. trochę co otrzymuję prezentuje kontroler Ale ekran się nie pojawia. nagle zostaje zwolniony. Czy trzeba zaimplementować inny kod? proszę zasugerować
Seethalakshmi_user8943692

13

dla SWIFT

func topMostController() -> UIViewController {
    var topController: UIViewController = UIApplication.sharedApplication().keyWindow!.rootViewController!
    while (topController.presentedViewController != nil) {
        topController = topController.presentedViewController!
    }
    return topController
}

1
Dziękuję Ci! To była jedyna rzecz, która rozwiązała ten błąd. Próbowałem przedstawić modalne VC z funkcji wyzwalanej przez rozpoznawanie gestów, dobrze po załadowaniu widoku (w Xcode 8 / iOS 10). O dziwo, topControllerzwracana przez tę funkcję jest dokładnie taka sama VC jak selfw przypadku próby prezentacji bezpośrednio przy użyciu self.presentViewController(myModalVC), ale nie ma błędu hierarchii widoku, gdy przekazuję prezentację VC za pomocą twojej funkcji.
Natalia

13

Wystarczy wykonać selektor z opóźnieniem - (działa 0 sekund).

override func viewDidLoad() {
    super.viewDidLoad()
    perform(#selector(presentExampleController), with: nil, afterDelay: 0)
}

@objc private func presentExampleController() {
    let exampleStoryboard = UIStoryboard(named: "example", bundle: nil)
    let exampleVC = storyboard.instantiateViewController(withIdentifier: "ExampleVC") as! ExampleVC
    present(exampleVC, animated: true) 
}

Wielkie dzięki. Utknąłem w tym problemie ponad godzinę.
Reza

Dziękuję ... @objc func presentExampleController ()
William Choy

Myślę, że to niesamowita odpowiedź!
mazend

To jest hack (który może teraz zadziałać), ale skonfiguruje Cię na coś, co zepsuje się w nieoczekiwanych przypadkach.
William Grand

6

Szybki 4

func topMostController() -> UIViewController {
    var topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController!
    while (topController.presentedViewController != nil) {
        topController = topController.presentedViewController!
    }
    return topController
}

1
Wyjaśnienie i przykład byłyby pomocne @AllenWixted
Eury Pérez Beltré

2

Dla Swift 3.0 i nowszych

public static func getTopViewController() -> UIViewController?{
if var topController = UIApplication.shared.keyWindow?.rootViewController
{
  while (topController.presentedViewController != nil)
  {
    topController = topController.presentedViewController!
  }
  return topController
}
return nil}

2

Otrzymałem ten błąd podczas prezentowania kontrolera po otwarciu przez użytkownika głębokiego linku. Wiem, że to nie jest najlepsze rozwiązanie, ale jeśli jesteś w krótkim czasie, tutaj jest szybka poprawka - po prostu zawiń kod asyncAfter:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.7, execute: { [weak self] in
                                navigationController.present(signInCoordinator.baseController, animated: animated, completion: completion)
                            })

Daje to czas na wywołanie kontrolera prezentacji viewDidAppear.


1
let storyboard = UIStoryboard(name: "test", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "teststoryboard") as UIViewController
UIApplication.shared.keyWindow?.rootViewController?.present(vc, animated: true, completion: nil)

Wydawało się, że to działa, aby upewnić się, że jest to najwyższy widok.

Otrzymałem błąd

Ostrzeżenie: próba przedstawienia myapp.testController: 0x7fdd01703990 na myapp.testController: 0x7fdd01703690, którego widok nie znajduje się w hierarchii okien!

Mam nadzieję, że pomoże to innym w szybkim 3


1

Próbowałem tak wielu sposobów! jedyną przydatną rzeczą jest:

if var topController = UIApplication.shared.keyWindow?.rootViewController
{
  while (topController.presentedViewController != nil)
  {
    topController = topController.presentedViewController!
  }
}

1

Wszystkie implementacje dla topViewController tutaj nie w pełni obsługują przypadki, gdy masz UINavigationControllerlub UITabBarController, dla tych dwóch, potrzebujesz nieco innej obsługi:

Do UITabBarControlleri UINavigationControllerpotrzebujesz innej implementacji.

Oto kod, którego używam do pobrania topMostViewController:

protocol TopUIViewController {
    func topUIViewController() -> UIViewController?
}

extension UIWindow : TopUIViewController {
    func topUIViewController() -> UIViewController? {
        if let rootViewController = self.rootViewController {
            return self.recursiveTopUIViewController(from: rootViewController)
        }

        return nil
    }

    private func recursiveTopUIViewController(from: UIViewController?) -> UIViewController? {
        if let topVC = from?.topUIViewController() { return recursiveTopUIViewController(from: topVC) ?? from }
        return from
    }
}

extension UIViewController : TopUIViewController {
    @objc open func topUIViewController() -> UIViewController? {
        return self.presentedViewController
    }
}

extension UINavigationController {
    override open func topUIViewController() -> UIViewController? {
        return self.visibleViewController
    }
}

extension UITabBarController {
    override open func topUIViewController() -> UIViewController? {
        return self.selectedViewController ?? presentedViewController
    }
}

1

Poprzednie odpowiedzi dotyczą sytuacji, w której kontroler widoku, który powinien przedstawiać widok 1) nie został jeszcze dodany do hierarchii widoków, lub 2) nie jest kontrolerem widoku z góry.
Inną możliwością jest to, że ostrzeżenie powinno być prezentowane, gdy inny ostrzeżenie jest już prezentowane i jeszcze nie zostało odrzucone.


1

Swift Method i dostarcz demo.

func topMostController() -> UIViewController {
    var topController: UIViewController = UIApplication.sharedApplication().keyWindow!.rootViewController!
    while (topController.presentedViewController != nil) {
        topController = topController.presentedViewController!
    }
    return topController
}

func demo() {
    let vc = ViewController()
    let nav = UINavigationController.init(rootViewController: vc)
    topMostController().present(nav, animated: true, completion: nil)
}

1

Swift 5.1 :

let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let mainViewController = storyboard.instantiateViewController(withIdentifier: "ID")
let appDeleg = UIApplication.shared.delegate as! AppDelegate
let root = appDeleg.window?.rootViewController as! UINavigationController
root.pushViewController(mainViewController, animated: true)

1

Użycie głównego wątku do prezentowania i zamykania kontrolera widoku zadziałało dla mnie.

DispatchQueue.main.async { self.present(viewController, animated: true, completion: nil) }

0

Zamiast znajdować kontroler widoku z góry, można użyć

viewController.modalPresentationStyle = UIModalPresentationStyle.currentContext

Gdzie viewController jest kontrolerem który chcesz zaprezentować Jest to przydatne gdy w hierarchii są różne rodzaje widoków jak TabBar, NavBar chociaż inne wydają się być poprawne ale bardziej hakerskie

Inny styl prezentacji można znaleźć w Apple Doc

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.