Ios 尝试在视图不在窗口层次结构中的UIViewController上显示UIViewController

Ios 尝试在视图不在窗口层次结构中的UIViewController上显示UIViewController,ios,cocoa-touch,ios6,views,hierarchy,Ios,Cocoa Touch,Ios6,Views,Hierarchy,刚开始使用Xcode 4.5,我在控制台中遇到以下错误: 警告:尝试在其视图不在窗口层次结构中的上显示 视图仍在显示中,应用程序中的所有内容都工作正常。这是iOS 6中的新功能吗 这是我用来在视图之间更改的代码: UIStoryboard *storyboard = self.storyboard; finishViewController *finished = [storyboard instantiateViewControllerWithIdentifier:@"finishViewC

刚开始使用Xcode 4.5,我在控制台中遇到以下错误:

警告:尝试在其视图不在窗口层次结构中的上显示

视图仍在显示中,应用程序中的所有内容都工作正常。这是iOS 6中的新功能吗

这是我用来在视图之间更改的代码:

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

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

您从何处调用此方法?我遇到了一个问题,我试图在
viewDidLoad
方法中显示一个模态视图控制器。我的解决方案是将此调用移动到
viewdide:
方法

我的假设是,视图控制器的视图在加载时(发送
viewdiload
消息时)不在窗口的视图层次结构中,但在显示后(发送
viewdilead:
消息时),它在窗口层次结构中


小心

如果在
viewdide:
中调用
presentViewController:animated:completion:
,您可能会遇到这样一个问题,即每当视图控制器的视图出现时,总是显示模态视图控制器(这很有意义!),因此显示的模态视图控制器将永远不会消失


也许这不是展示模态视图控制器的最佳场所,或者可能需要保留一些附加状态,以允许显示视图控制器决定是否立即显示模式视图控制器。

我在
viewDidLoad
中尝试显示
UIViewController
时也遇到了这个问题。詹姆斯·贝德福德(James Bedford)的回答有效,但我的应用程序先显示背景,持续1到2秒

经过一些研究,我找到了一种使用
addChildViewController
解决这个问题的方法

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

viewWillLayoutSubviews
viewdillayoutsubviews
(iOS 5.0+)可用于此目的。它们的调用时间比viewdide出现的时间早。

可能像我一样,您的根目录是错误的
viewController

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
    }
}
我想在
非UIViewController
上下文中显示
ViewController

所以我不能使用这样的代码:

[self presentViewController:]
[[[[UIApplication sharedApplication] delegate] window] rootViewController]
因此,我得到了一个UIViewController:

[self presentViewController:]
[[[[UIApplication sharedApplication] delegate] window] rootViewController]

由于某种原因(逻辑错误),
rootViewController
不是预期的(正常的
UIViewController
)。然后我纠正了错误,将
rootViewController
替换为
UINavigationController
,问题就消失了。

我也遇到了这个问题,但它与时间无关。我使用单例处理场景,并将其设置为演示者。换句话说,“自我”与任何东西都没有联系。我刚刚把它的内部“场景”变成了新的主持人,瞧,它成功了。(瞧,在你了解了它的含义后,它失去了联系,呵呵)


所以,是的,这不是“神奇地找到正确的方法”,而是理解代码的位置和作用。我很高兴苹果公司给出了这样一个简单的英语警告信息,甚至带着感情。感谢做了这件事的苹果开发人员

我也有同样的问题。我必须嵌入一个导航控制器,并通过它呈现控制器。下面是示例代码

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

}

另一个潜在原因:

当我不小心两次演示同一个视图控制器时,我遇到了这个问题。(一次使用
performsguewithidentifer:sender:
,当按下按钮时调用,第二次使用直接连接到按钮的segue)


实际上,两个分段同时触发,我得到了一个错误:
尝试在Y上显示X,其视图不在窗口层次结构中

如果播放视频时有AVPlayer对象,则必须先暂停视频。

TL;DR您只能有一个rootViewController,它是最近出现的一个。因此,不要试图让一个viewcontroller呈现另一个viewcontroller,因为它已经呈现了一个未被忽略的viewcontroller

在做了一些我自己的测试之后,我得出了一个结论

如果您有一个rootViewController,希望显示所有内容,那么您可能会遇到此问题

这是我的根控制器代码(open是我从根显示viewcontroller的快捷方式)

如果我连续调用open两次(不管经过了多长时间),那么在第一次打开时就可以了,但在第二次打开时就不行了。第二次打开尝试将导致上述错误

但是,如果关闭最近显示的视图,然后调用“打开”,则在再次调用“打开”(在另一个viewcontroller上)时,它可以正常工作


我得出的结论是,只有最近调用的rootViewController位于视图层次结构上(即使您没有关闭它或删除视图)。我试着处理所有的加载程序调用(viewDidLoad、viewDidAppear和执行延迟调度调用),我发现让它工作的唯一方法是只从最顶层的视图控制器调用present

我也有同样的问题。问题是,performsguewithidentifier是由一个通知触发的,当我将通知放在主线程上时,警告消息就消失了

从内嵌在容器中的视图控制器执行segue时,也会收到此警告。正确的解决方案是从容器的父级使用segue,而不是从容器的视图控制器使用segue。

必须写在第行下面

func close(controller:UIViewController)
{
    ROOT_VIEW_CONTROLLER.dismissViewControllerAnimated(true, completion: nil)
}
self.searchController.definesPresentationContext = true
而不是

self.definesPresentationContext = true

在UIViewController

中,考虑到您想在几乎任何地方显示一些
viewController,我最终得到了这样一个代码,它最终对我有效(Swift)。当没有rootViewController a时,此代码显然会崩溃
UIViewController *yourCurrentViewController = [UIApplication sharedApplication].keyWindow.rootViewController;

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

[yourCurrentViewController dismissViewControllerAnimated:YES completion:nil];
UIViewController *top = [UIApplication sharedApplication].keyWindow.rootViewController;
[top presentViewController:secondView animated:YES completion: nil];
/// 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
[AlertWindow presentAlertWithTitle:@"My title" message:@"My message"];
self.tabBarController.dismiss(animated: false) {
  self.start()
}
@IBAction func unwindFromAuthenticationWithSegue(segue: UIStoryboardSegue) {
    self.shouldSegueToMainTabBar = true
}

@IBAction func unwindFromForgetPasswordWithSegue(segue: UIStoryboardSegue) {
    self.shouldSegueToLogin = true
}
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
    }
}
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)
}
func updateView() {

 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in

// for programmatically presenting view controller 
// present(viewController, animated: true, completion: nil)

//For Story board segue. you will also have to setup prepare segue for this to work. 
 self?.performSegue(withIdentifier: "Identifier", sender: nil)
  }
}
if var topController = UIApplication.shared.keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }
    topController.present(controller, animated: false, completion: nil)
    // topController should now be your topmost view controller
}
override func viewDidLoad() {
    super.viewDidLoad()
    OperationQueue.main.addOperation {
        // push or present the page inside this block
    }
}
present(alert, animated: true, completion: nil)
    
DispatchQueue.main.async { [weak self] in
    self?.present(alert, animated: true, completion: nil)
}