Ios 尝试在视图不在窗口层次结构中的UIViewController上显示UIViewController
刚开始使用Xcode 4.5,我在控制台中遇到以下错误: 警告:尝试在其视图不在窗口层次结构中的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
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)
}