刚开始使用Xcode 4.5,我在控制台得到了这个错误:
警告:试图在< ViewController: 0x1ec3e000>上显示< finishViewController: 0x1e56e0a0 >,其视图不在窗口层次结构中!
视图仍在显示,应用程序中的一切都在正常工作。这是iOS 6的新功能吗?
这是我用来在视图之间更改的代码:
UIStoryboard *storyboard = self.storyboard;
finishViewController *finished =
[storyboard instantiateViewControllerWithIdentifier:@"finishViewController"];
[self presentViewController:finished animated:NO completion:NULL];
在主窗口中,可能总是会出现与显示警报不兼容的过渡。为了允许在应用程序生命周期的任何时候显示警报,您应该有一个单独的窗口来完成这项工作。
/// 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"];
我已经结束了这样一个代码,最终工作给我(Swift),考虑到你想要显示一些viewController从几乎任何地方。当没有可用的rootViewController时,这段代码显然会崩溃,这是开放式结局。它也不包括通常需要切换到UI线程使用
dispatch_sync(dispatch_get_main_queue(), {
guard !NSBundle.mainBundle().bundlePath.hasSuffix(".appex") else {
return; // skip operation when embedded to App Extension
}
if let delegate = UIApplication.sharedApplication().delegate {
delegate.window!!.rootViewController?.presentViewController(viewController, animated: true, completion: { () -> Void in
// optional completion code
})
}
}
如果其他解决方案因为某些原因看起来不太好,你仍然可以使用这个不错的老方法,用延迟0表示,就像这样:
dispatch_after(0, dispatch_get_main_queue(), ^{
finishViewController *finished = [self.storyboard instantiateViewControllerWithIdentifier:@"finishViewController"];
[self presentViewController:finished animated:NO completion:NULL];
});
虽然我没有看到任何文件保证你的VC会在调度块计划执行的时间上处于视图层次结构上,但我观察到它会工作得很好。
使用延迟,例如0.2秒也是一种选择。最好的事情-这样你就不需要在viewDidAppear中混乱布尔变量:
Swift 5 -后台线程
如果一个警报控制器在后台线程上执行,那么“Attempt to present…”“其视图不在窗口层次结构中”的错误可能会发生。
所以这个:
present(alert, animated: true, completion: nil)
是这样固定的:
DispatchQueue.main.async { [weak self] in
self?.present(alert, animated: true, completion: nil)
}
可能和我一样,你有一个错误的根viewController
我想在非uiviewcontroller上下文中显示一个ViewController,
所以我不能使用这样的代码:
[self presentViewController:]
我得到一个UIViewController
[[[[UIApplication sharedApplication] delegate] window] rootViewController]
由于某种原因(逻辑错误),rootViewController是其他的东西比预期的(一个正常的UIViewController)。然后我纠正了这个错误,用UINavigationController替换了rootViewController,问题就解决了。