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.

Nils Wasell
źródło
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.

Acey
źródło
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;
}
akr ios
źródło
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.

Jason
źródło
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)
Jacob F. Davis C-CISO
źródło
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
}
Viennarz Valdevieso Curtiz
źródło
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) 
}
Edward
źródło
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
}
Allen Wixted
źródło
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}
Hiren Panchal
źródło
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.

Bohdan Savych
źródło
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

Jason
źródło
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!
  }
}
Xiaohan Chen
źródło
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
    }
}
Grzegorz Krukowski
źródło
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.

Reinhard Männer
źródło
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)
}
Zgpeace
źródło
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)
Bavafaali
źródło
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) }
dinesh sharma
źródło
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

Achil tato
źródło