AlertController 不在 window 层次结构中
AlertController is not in the window hierarchy
我刚刚使用 ViewController class 创建了一个单视图应用程序项目。我想从位于我自己的 class 中的函数显示一个 UIAlertController。
这是我的 class 警报。
class AlertController: UIViewController {
func showAlert() {
var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
self.presentViewController(alert, animated: true, completion: nil)
}
}
这是执行警报的 ViewController。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func showAlertButton(sender: AnyObject) {
var alert = AlertController()
alert.showAlert()
}
}
这是我得到的,而不是漂亮的警报。
警告:尝试在 Sprint1.AlertController 上呈现 UIAlertController:0x797d2d20:0x797cc500,其视图不在 window 层次结构中!
我该怎么办?
让我们看看您的视图层次结构。你有一个ViewController
。
然后你正在创建一个 AlertController
,你没有将它添加到你的层次结构中,而是在它上面调用一个实例方法,它试图使用 AlertController
作为呈现控制器来显示另一个控制器(UIAlertController
).
+ ViewController
+ AlertController (not in hierarchy)
+ UIAlertController (cannot be presented from AlertController)
简化您的代码
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func showAlertButton(sender: AnyObject) {
var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
self.presentViewController(alert, animated: true, completion: nil)
}
}
这会起作用。
如果您需要 AlertController
的东西,您必须先将它添加到层次结构中,例如使用 addChildViewController
或使用另一个 presentViewController
调用。
如果您希望 class 只是创建警报的助手,它应该如下所示:
class AlertHelper {
func showAlert(fromController controller: UIViewController) {
var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
controller.presentViewController(alert, animated: true, completion: nil)
}
}
称为
var alert = AlertHelper()
alert.showAlert(fromController: self)
如果你想创建一个单独的 class 来显示这样的警报,subclass NSObject 而不是 UIViewController。
并将启动它的 ViewControllers 引用传递给 showAlert 函数,以便您可以在那里显示警报视图。
写下面3行,我们要做的就是这个
Swift 3.0
private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: flag, completion: completion)
}
Swift 2.0
private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alert, animated: flag, completion: completion)
}
如果您从模态控制器实例化您的 UIAlertController
,您需要在 viewDidAppear
中执行,而不是在 viewDidLoad
中执行,否则您会收到错误消息。
这是我的代码 (Swift 4):
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let alertController = UIAlertController(title: "Foo", message: "Bar", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
}
这是 Swift3 中 Utility.swift class(不是 UIViewController)中 UIAlertController 的代码,谢谢 Mitsuaki!
private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: flag, completion: completion)
}
func warningAlert(title: String, message: String ){
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: { (action) -> Void in
}))
// self.present(alert, animated: true, completion: nil)
presentViewController(alert: alert, animated: true, completion: nil)
}
您可以使用以下函数从任何地方调用警报,只需在 AnyClass 中包含这些方法
class func topMostController() -> UIViewController {
var topController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
while ((topController?.presentedViewController) != nil) {
topController = topController?.presentedViewController
}
return topController!
}
class func alert(message:String){
let alert=UIAlertController(title: "AppName", message: message, preferredStyle: .alert);
let cancelAction: UIAlertAction = UIAlertAction(title: "OK", style: .cancel) { action -> Void in
}
alert.addAction(cancelAction)
AnyClass.topMostController().present(alert, animated: true, completion: nil);
}
然后调用
AnyClass.alert(message:"Your Message")
它帮助我在 viewDidLoad 方法和触发 alert 方法之间稍作延迟:
[self performSelector:@selector(checkPhotoPermission) withObject:nil afterDelay:0.1f];
这对我有用:
- (UIViewController *)topViewController{
return [self topViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController *)topViewController:(UIViewController *)rootViewController
{
if (rootViewController.presentedViewController == nil) {
return rootViewController;
}
if ([rootViewController.presentedViewController isMemberOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self topViewController:lastViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self topViewController:presentedViewController];
}
实施:
UIViewController * topViewController = [self topViewController];
与警报一起使用:
[topViewController presentViewController:yourAlert animated:YES completion:nil];
您可以从应用中的任何 class 发送提醒(使用 UIKit:#import <UIKit/UIKit.h>
)
// I always find it helpful when you want to alert from anywhere it's codebase
// if you find the error above mentioned in the question' title.
let controller = UIAlertController(title: "", message: "Alert!", preferredStyle: UIAlertController.Style.alert)
let action = UIAlertAction(title: "Cancel" , style: UIAlertAction.Style.cancel, handler: nil)
controller.addAction(action)
// Find Root View Controller
var rootVC = UIApplication.shared.windows.first?.rootViewController
if var topController = rootVC {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
rootVC = topController
}
rootVC?.present(controller, animated: true, completion: nil)
我刚刚使用 ViewController class 创建了一个单视图应用程序项目。我想从位于我自己的 class 中的函数显示一个 UIAlertController。
这是我的 class 警报。
class AlertController: UIViewController {
func showAlert() {
var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
self.presentViewController(alert, animated: true, completion: nil)
}
}
这是执行警报的 ViewController。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func showAlertButton(sender: AnyObject) {
var alert = AlertController()
alert.showAlert()
}
}
这是我得到的,而不是漂亮的警报。
警告:尝试在 Sprint1.AlertController 上呈现 UIAlertController:0x797d2d20:0x797cc500,其视图不在 window 层次结构中!
我该怎么办?
让我们看看您的视图层次结构。你有一个ViewController
。
然后你正在创建一个 AlertController
,你没有将它添加到你的层次结构中,而是在它上面调用一个实例方法,它试图使用 AlertController
作为呈现控制器来显示另一个控制器(UIAlertController
).
+ ViewController
+ AlertController (not in hierarchy)
+ UIAlertController (cannot be presented from AlertController)
简化您的代码
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func showAlertButton(sender: AnyObject) {
var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
self.presentViewController(alert, animated: true, completion: nil)
}
}
这会起作用。
如果您需要 AlertController
的东西,您必须先将它添加到层次结构中,例如使用 addChildViewController
或使用另一个 presentViewController
调用。
如果您希望 class 只是创建警报的助手,它应该如下所示:
class AlertHelper {
func showAlert(fromController controller: UIViewController) {
var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
controller.presentViewController(alert, animated: true, completion: nil)
}
}
称为
var alert = AlertHelper()
alert.showAlert(fromController: self)
如果你想创建一个单独的 class 来显示这样的警报,subclass NSObject 而不是 UIViewController。
并将启动它的 ViewControllers 引用传递给 showAlert 函数,以便您可以在那里显示警报视图。
写下面3行,我们要做的就是这个
Swift 3.0
private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: flag, completion: completion)
}
Swift 2.0
private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alert, animated: flag, completion: completion)
}
如果您从模态控制器实例化您的 UIAlertController
,您需要在 viewDidAppear
中执行,而不是在 viewDidLoad
中执行,否则您会收到错误消息。
这是我的代码 (Swift 4):
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let alertController = UIAlertController(title: "Foo", message: "Bar", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
}
这是 Swift3 中 Utility.swift class(不是 UIViewController)中 UIAlertController 的代码,谢谢 Mitsuaki!
private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: flag, completion: completion)
}
func warningAlert(title: String, message: String ){
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: { (action) -> Void in
}))
// self.present(alert, animated: true, completion: nil)
presentViewController(alert: alert, animated: true, completion: nil)
}
您可以使用以下函数从任何地方调用警报,只需在 AnyClass 中包含这些方法
class func topMostController() -> UIViewController {
var topController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
while ((topController?.presentedViewController) != nil) {
topController = topController?.presentedViewController
}
return topController!
}
class func alert(message:String){
let alert=UIAlertController(title: "AppName", message: message, preferredStyle: .alert);
let cancelAction: UIAlertAction = UIAlertAction(title: "OK", style: .cancel) { action -> Void in
}
alert.addAction(cancelAction)
AnyClass.topMostController().present(alert, animated: true, completion: nil);
}
然后调用
AnyClass.alert(message:"Your Message")
它帮助我在 viewDidLoad 方法和触发 alert 方法之间稍作延迟:
[self performSelector:@selector(checkPhotoPermission) withObject:nil afterDelay:0.1f];
这对我有用:
- (UIViewController *)topViewController{
return [self topViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController *)topViewController:(UIViewController *)rootViewController
{
if (rootViewController.presentedViewController == nil) {
return rootViewController;
}
if ([rootViewController.presentedViewController isMemberOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self topViewController:lastViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self topViewController:presentedViewController];
}
实施:
UIViewController * topViewController = [self topViewController];
与警报一起使用:
[topViewController presentViewController:yourAlert animated:YES completion:nil];
您可以从应用中的任何 class 发送提醒(使用 UIKit:#import <UIKit/UIKit.h>
)
// I always find it helpful when you want to alert from anywhere it's codebase
// if you find the error above mentioned in the question' title.
let controller = UIAlertController(title: "", message: "Alert!", preferredStyle: UIAlertController.Style.alert)
let action = UIAlertAction(title: "Cancel" , style: UIAlertAction.Style.cancel, handler: nil)
controller.addAction(action)
// Find Root View Controller
var rootVC = UIApplication.shared.windows.first?.rootViewController
if var topController = rootVC {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
rootVC = topController
}
rootVC?.present(controller, animated: true, completion: nil)