Предупреждение: Попытка представить * на *, чей вид не входит в иерархию окон - swift

83

Я пытаюсь представить, есть ViewController ли какие-либо сохраненные данные в модели данных. Но я получаю следующую ошибку:

Предупреждение: Попытка представить * на *, чей вид не входит в иерархию окон "

Соответствующий код:

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

Я безуспешно пробовал различные решения, найденные с помощью Google.

Нильс Васелл
источник
Вы подключили свой ViewController в раскадровке? Вы здесь используете NavigationController?
derdida
Что вы имеете в виду, если я подключил свою ViewControllerв Storyboard? Я не использую NavigationController@derdida
Нильс Уэзелл
Попробуйте: self.addChildViewController (childController: UIViewController) в ViewDidLoad
derdida
Это вызывает ошибку. Он ожидает имени члена или вызова конструктора после «имени типа» ( UIViewControllerчасти). @derdida
Nils Wasell
Где ты это написал? В ViewDidLoad вашего MainViewController?
derdida

Ответы:

127

На данный момент в вашем коде представление контроллера представления только создано, но не добавлено ни в какую иерархию представлений. Если вы хотите сделать презентацию из этого контроллера представления как можно скорее, сделайте это viewDidAppearкак можно безопаснее.

Эйси
источник
2
Если я помещу его в viewDidAppear, то, когда я отклоняю представленныйViewController, он снова вызовет метод viewDidAppear и снова представит viewController. У меня есть обходной путь: поставить условный оператор перед его представлением, но есть ли способ вместо того, чтобы Условный оператор?
Puneet
2
Я бы сказал, что попытка представить другое модальное окно при появлении экрана - это странное поведение для начала, поэтому логическое значение состояния вряд ли является худшим решением. В то же время подумайте о корневой причине, по которой вам нужно отображать модальное окно, и посмотрите, можете ли вы получить доступ к этой информации.
Acey 07
2
Если вы сделаете это в viewWillAppear, у вас будет ситуация, когда он находится в середине презентации, когда вы начинаете презентацию. Альтернативой было бы поместить его в viewWillAppear, но использовать свойство transitionCoordinator, новое в iOS 8, чтобы добавить код в завершение перехода. Более подробную информацию можно найти здесь developer.apple.com/library/prerelease/ios/documentation/UIKit/…
Acey
36

В объекте c: это решило мою проблему при представлении контроллера просмотра поверх mpmovieplayer

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

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

    return topController;
}
Akr ios
источник
34

Swift 3

Я постоянно вспоминал об этом как новичок и обнаружил, что в настоящее время загружаются модальные представления, которые можно отклонить, но переключение на корневой контроллер лучше всего, если вам не нужно показывать модальные окна.

Я использовал это

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

Используя это вместо моего 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

Просто настройте контроллер представления, если вам нужно переключаться между несколькими экранами раскадровки.

Джейсон
источник
16

Свифт 3.

Вызовите эту функцию, чтобы получить самый верхний контроллер представления, а затем добавьте этот контроллер представления.

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

Применение:

let topVC = topMostController()
let vcToPresent = self.storyboard!.instantiateViewController(withIdentifier: "YourVCStoryboardID") as! YourViewController
topVC.present(vcToPresent, animated: true, completion: nil)
Джейкоб Ф. Дэвис C-директор по информационной безопасности
источник
1
Спасибо @Jacob Davis за это! Я использовал его, чтобы представить alertController, потому что я получал предупреждение: попытка представить ....
Гэри Мэнстед,
Это очень полезно. кое-что представляю, представляю контроллер Но экран не появляется. внезапно его уволили. Любой другой код необходимо реализовать? пожалуйста, предложите
Seethalakshmi_user8943692
13

для SWIFT

func topMostController() -> UIViewController {
    var topController: UIViewController = UIApplication.sharedApplication().keyWindow!.rootViewController!
    while (topController.presentedViewController != nil) {
        topController = topController.presentedViewController!
    }
    return topController
}
Виеннарц Валдевьесо Куртиз
источник
1
Спасибо! Это единственное, что помогло мне решить эту ошибку. Я пытался представить модальный VC из функции, запускаемой распознавателем жестов, после полной загрузки представления (в Xcode 8 / iOS 10). Как ни странно, topControllerэта функция возвращает тот же самый VC, что и selfпри попытке представления напрямую с использованием self.presentViewController(myModalVC), но при передаче представляющего VC с использованием вашей функции ошибки иерархии представлений не возникает.
Наталья
13

Просто нужно выполнить селектор с задержкой - (работает 0 секунд).

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) 
}
Эдвард
источник
Большое спасибо. Я застрял в этой проблеме больше часа.
Реза
Спасибо ... @objc func presentExampleController ()
Уильям Чой
Я думаю, это потрясающий ответ!
mazend
Это хакер (который сейчас может работать), но он настроит вас на то, что сломается в неожиданных случаях.
Уильям Гранд
6

Swift 4

func topMostController() -> UIViewController {
    var topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController!
    while (topController.presentedViewController != nil) {
        topController = topController.presentedViewController!
    }
    return topController
}
Аллен Викстед
источник
1
Объяснение и пример были бы полезны @AllenWixted
Eury Pérez Beltré
2

Для Swift 3.0 и выше

public static func getTopViewController() -> UIViewController?{
if var topController = UIApplication.shared.keyWindow?.rootViewController
{
  while (topController.presentedViewController != nil)
  {
    topController = topController.presentedViewController!
  }
  return topController
}
return nil}
Хирен Панчал
источник
2

Я получал эту ошибку, когда представлял контроллер после того, как пользователь открыл deeplink. Я знаю, что это не лучшее решение, но если у вас короткие сроки, вот быстрое решение - просто оберните свой код в asyncAfter:

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

Это даст время для звонка вашему представляющему контроллеру 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)

Казалось, это сработало, чтобы убедиться, что это самый верхний вид.

Я получал ошибку

Предупреждение: Попытка представить myapp.testController: 0x7fdd01703990 на myapp.testController: 0x7fdd01703690, представление которого не находится в иерархии окон!

Надеюсь, это поможет другим с быстрым 3

Джейсон
источник
1

Я перепробовала столько подходов! единственная полезная вещь:

if var topController = UIApplication.shared.keyWindow?.rootViewController
{
  while (topController.presentedViewController != nil)
  {
    topController = topController.presentedViewController!
  }
}
Сяохань Чен
источник
1

Все реализации для topViewController здесь не полностью поддерживают случаи, когда у вас есть UINavigationControllerилиUITabBarController для этих двух вам нужна немного другая обработка:

Для UITabBarControllerиUINavigationController вам нужна другая реализация.

Вот код, который я использую для получения 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

Предыдущие ответы относятся к ситуации, когда контроллер представления, который должен представлять представление 1) еще не добавлен в иерархию представлений, или 2) не является контроллером представления сверху.
Другая возможность заключается в том, что предупреждение должно быть представлено, когда другое предупреждение уже представлено, но еще не закрыто.

Рейнхард Мэннер
источник
1

Swift Method и предоставьте демонстрацию.

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
источник
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

У меня сработало использование основного потока для представления и отключения контроллера представления.

DispatchQueue.main.async { self.present(viewController, animated: true, completion: nil) }
Динеш Шарма
источник
0

Вместо поиска контроллера вида сверху можно использовать

viewController.modalPresentationStyle = UIModalPresentationStyle.currentContext

Где viewController - это контроллер, который вы хотите представить. Это полезно, когда есть разные виды представлений в иерархии, такие как TabBar, NavBar, хотя другие кажутся правильными, но более хакерскими

Другой стиль презентации можно найти в документе Apple.

Ахил Папа
источник