检查 UIAlertController 是否已经呈现的最佳方法是什么?
What is the best way to check if a UIAlertController is already presenting?
我有一个表视图,当加载时,每个单元格可能 return 一个 NSError,我选择在 UIAlertController 中显示它。问题是,如果 returned.
有多个错误,我会在控制台中收到此错误
Warning: Attempt to present UIAlertController: 0x14e64cb00 on MessagesMasterVC: 0x14e53d800 which is already presenting (null)
理想情况下,我希望在我的 UIAlertController 扩展方法中处理这个问题。
class func simpleAlertWithMessage(message: String!) -> UIAlertController {
let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let cancel = UIAlertAction(title: "Ok", style: .Cancel, handler: nil)
alertController.addAction(cancel)
return alertController
}
根据 matt 的回答,我将扩展更改为 UIViewController 扩展,它更简洁并节省了大量 presentViewController 代码。
func showSimpleAlertWithMessage(message: String!) {
let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let cancel = UIAlertAction(title: "Ok", style: .Cancel, handler: nil)
alertController.addAction(cancel)
if self.presentedViewController == nil {
self.presentViewController(alertController, animated: true, completion: nil)
}
}
"already presenting" 不是 UIAlertController,而是 MessagesMasterVC。一个视图控制器一次只能呈现另一个视图控制器。因此出现错误消息。
换句话说,如果您已经告诉视图控制器presentViewController:...
,您不能再这样做,直到呈现的视图控制器被关闭。
您可以通过检查 presentedViewController
来询问 MessagesMasterVC 是否已经在呈现视图控制器。如果不是 nil
,请不要告诉它 presentViewController:...
- 它已经在呈现一个视图控制器。
if ([self.navigationController.visibleViewController isKindOfClass:[UIAlertController class]]) {
// UIAlertController is presenting.Here
}
嗯,从我的角度来看,上面建议的解决方案有一个本质问题:
如果你问你的ViewController,属性'presentedViewController'是否为nil,答案是false,你不能得出结论,你的UIAlertController已经呈现了。它可以是任何呈现的 ViewController,例如弹出窗口。所以我的建议是一定要检查,警报是否已经在屏幕上如下(将呈现的 ViewController 转换为 UIAlertController):
if self.presentedViewController == nil {
// do your presentation of the UIAlertController
// ...
} else {
// either the Alert is already presented, or any other view controller
// is active (e.g. a PopOver)
// ...
let thePresentedVC : UIViewController? = self.presentedViewController as UIViewController?
if thePresentedVC != nil {
if let thePresentedVCAsAlertController : UIAlertController = thePresentedVC as? UIAlertController {
// nothing to do , AlertController already active
// ...
print("Alert not necessary, already on the screen !")
} else {
// there is another ViewController presented
// but it is not an UIAlertController, so do
// your UIAlertController-Presentation with
// this (presented) ViewController
// ...
thePresentedVC!.presentViewController(...)
print("Alert comes up via another presented VC, e.g. a PopOver")
}
}
}
这个类别可以自动管理所有模态控制器,包括 UIAlertController。
只需关闭当前控制器并显示您想要的控制器
即
self.dismiss(animated: false, completion: nil)
self.displayAlertController()
这是我在 Swift 中使用的解决方案 3. 这是一个向用户显示警报的函数,如果在用户关闭警报之前多次调用它,它将添加新的已显示的警报的警报文本。如果正在显示其他视图,则不会出现警报。并非所有人都会同意这种行为,但它适用于简单的情况。
extension UIViewController {
func showAlert(_ msg: String, title: String = "") {
if let currentAlert = self.presentedViewController as? UIAlertController {
currentAlert.message = (currentAlert.message ?? "") + "\n\nUpdate:\(title): \(msg)"
return
}
// create the alert
let alert = UIAlertController(title: title, message: msg, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
// show the alert
self.present(alert, animated: true, completion: nil)
}
}
我们可以简单地检查是否显示了任何视图控制器。
如果出现,则检查它是否属于 UIAlertController 类型。
id alert = self.presentedViewController;
if (alert && [alert isKindOfClass:[UIAlertController class]])
{
*// YES UIAlertController is already presented*
}
else
{
// UIAlertController is not presented OR visible.
}
我用它来检测、删除和提醒。
首先我们使用以下函数创建一个警报。
var yourAlert :UIAlertController!
func useYouAlert (header: String, info:String){
yourAlert = UIAlertController(title:header as String, message: info as String, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: self.langText[62]as String, style: UIAlertActionStyle.default) { (result : UIAlertAction) -> Void in
print("OK")
}
yourAlert.addAction(okAction)
self.present(yourAlert.addAction, animated: true, completion: nil)
}
在您代码的其他部分
if yourAlert != nil {
yourAlert.dismiss(animated: true, completion: nil)
}
对于最新的 Swift 语言,您可以使用以下语言:
var alert = presentedViewController
if alert != nil && (alert is UIAlertController) {
// YES UIAlertController is already presented*
} else {
// UIAlertController is not presented OR visible.
}
Swift 4.2+ Answer
if UIApplication.topViewController()!.isKind(of: UIAlertController.self) {
print("UIAlertController is presented")}
对于那些不知道如何获得最顶级的人Viewcontroller
extension UIApplication {
public class func topViewController(_ base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}}
Swift 5+ Answer
'keyWindow' was deprecated in iOS 13.0
suggested edit
if UIApplication.topViewController()!.isKind(of: UIAlertController.self) {
print("UIAlertController is presented")}
对于那些不知道如何获得最顶级的人Viewcontroller
extension UIApplication {
public class func topViewController(_ base: UIViewController? = UIApplication.shared.windows.first?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}}
关闭当前控制器并像
一样呈现警报控制器
func alert(_ message:String) {
let alert = UIAlertController(title: "Error!", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
self.dismiss(animated: false, completion: nil)
self.present(alert, animated: true,completion: nil)
}
您可以在一行中测试是否已显示警报:
if self.presentedViewController as? UIAlertController != nil {
print ("alert already presented")
}
我发现我需要创建一个队列来堆叠 UIAlertController 请求。
NSMutableArray *errorMessagesToShow; // in @interface
errorMessagesToShow=[[NSMutableArray alloc] init]; // in init
-(void)showError:(NSString *)theErrorMessage{
if(theErrorMessage.length>0){
[errorMessagesToShow addObject:theErrorMessage];
[self showError1];
}
}
-(void)showError1{
NSString *theErrorMessage;
if([errorMessagesToShow count]==0)return; // queue finished
UIViewController* parentController =[[UIApplication sharedApplication]keyWindow].rootViewController;
while( parentController.presentedViewController &&
parentController != parentController.presentedViewController ){
parentController = parentController.presentedViewController;
}
if([parentController isKindOfClass:[UIAlertController class]])return; // busy
// construct the alert using [errorMessagesToShow objectAtIndex:0]
// add to each UIAlertAction completionHandler [self showError1];
// then
[errorMessagesToShow removeObjectAtIndex:0];
[parentController presentViewController:alert animated:YES completion:nil];
}
我有一个表视图,当加载时,每个单元格可能 return 一个 NSError,我选择在 UIAlertController 中显示它。问题是,如果 returned.
有多个错误,我会在控制台中收到此错误Warning: Attempt to present UIAlertController: 0x14e64cb00 on MessagesMasterVC: 0x14e53d800 which is already presenting (null)
理想情况下,我希望在我的 UIAlertController 扩展方法中处理这个问题。
class func simpleAlertWithMessage(message: String!) -> UIAlertController {
let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let cancel = UIAlertAction(title: "Ok", style: .Cancel, handler: nil)
alertController.addAction(cancel)
return alertController
}
根据 matt 的回答,我将扩展更改为 UIViewController 扩展,它更简洁并节省了大量 presentViewController 代码。
func showSimpleAlertWithMessage(message: String!) {
let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let cancel = UIAlertAction(title: "Ok", style: .Cancel, handler: nil)
alertController.addAction(cancel)
if self.presentedViewController == nil {
self.presentViewController(alertController, animated: true, completion: nil)
}
}
"already presenting" 不是 UIAlertController,而是 MessagesMasterVC。一个视图控制器一次只能呈现另一个视图控制器。因此出现错误消息。
换句话说,如果您已经告诉视图控制器presentViewController:...
,您不能再这样做,直到呈现的视图控制器被关闭。
您可以通过检查 presentedViewController
来询问 MessagesMasterVC 是否已经在呈现视图控制器。如果不是 nil
,请不要告诉它 presentViewController:...
- 它已经在呈现一个视图控制器。
if ([self.navigationController.visibleViewController isKindOfClass:[UIAlertController class]]) {
// UIAlertController is presenting.Here
}
嗯,从我的角度来看,上面建议的解决方案有一个本质问题:
如果你问你的ViewController,属性'presentedViewController'是否为nil,答案是false,你不能得出结论,你的UIAlertController已经呈现了。它可以是任何呈现的 ViewController,例如弹出窗口。所以我的建议是一定要检查,警报是否已经在屏幕上如下(将呈现的 ViewController 转换为 UIAlertController):
if self.presentedViewController == nil {
// do your presentation of the UIAlertController
// ...
} else {
// either the Alert is already presented, or any other view controller
// is active (e.g. a PopOver)
// ...
let thePresentedVC : UIViewController? = self.presentedViewController as UIViewController?
if thePresentedVC != nil {
if let thePresentedVCAsAlertController : UIAlertController = thePresentedVC as? UIAlertController {
// nothing to do , AlertController already active
// ...
print("Alert not necessary, already on the screen !")
} else {
// there is another ViewController presented
// but it is not an UIAlertController, so do
// your UIAlertController-Presentation with
// this (presented) ViewController
// ...
thePresentedVC!.presentViewController(...)
print("Alert comes up via another presented VC, e.g. a PopOver")
}
}
}
这个类别可以自动管理所有模态控制器,包括 UIAlertController。
只需关闭当前控制器并显示您想要的控制器 即
self.dismiss(animated: false, completion: nil)
self.displayAlertController()
这是我在 Swift 中使用的解决方案 3. 这是一个向用户显示警报的函数,如果在用户关闭警报之前多次调用它,它将添加新的已显示的警报的警报文本。如果正在显示其他视图,则不会出现警报。并非所有人都会同意这种行为,但它适用于简单的情况。
extension UIViewController {
func showAlert(_ msg: String, title: String = "") {
if let currentAlert = self.presentedViewController as? UIAlertController {
currentAlert.message = (currentAlert.message ?? "") + "\n\nUpdate:\(title): \(msg)"
return
}
// create the alert
let alert = UIAlertController(title: title, message: msg, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
// show the alert
self.present(alert, animated: true, completion: nil)
}
}
我们可以简单地检查是否显示了任何视图控制器。
如果出现,则检查它是否属于 UIAlertController 类型。
id alert = self.presentedViewController;
if (alert && [alert isKindOfClass:[UIAlertController class]])
{
*// YES UIAlertController is already presented*
}
else
{
// UIAlertController is not presented OR visible.
}
我用它来检测、删除和提醒。
首先我们使用以下函数创建一个警报。
var yourAlert :UIAlertController!
func useYouAlert (header: String, info:String){
yourAlert = UIAlertController(title:header as String, message: info as String, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: self.langText[62]as String, style: UIAlertActionStyle.default) { (result : UIAlertAction) -> Void in
print("OK")
}
yourAlert.addAction(okAction)
self.present(yourAlert.addAction, animated: true, completion: nil)
}
在您代码的其他部分
if yourAlert != nil {
yourAlert.dismiss(animated: true, completion: nil)
}
对于最新的 Swift 语言,您可以使用以下语言:
var alert = presentedViewController
if alert != nil && (alert is UIAlertController) {
// YES UIAlertController is already presented*
} else {
// UIAlertController is not presented OR visible.
}
Swift 4.2+ Answer
if UIApplication.topViewController()!.isKind(of: UIAlertController.self) {
print("UIAlertController is presented")}
对于那些不知道如何获得最顶级的人Viewcontroller
extension UIApplication {
public class func topViewController(_ base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}}
Swift 5+ Answer 'keyWindow' was deprecated in iOS 13.0 suggested edit
if UIApplication.topViewController()!.isKind(of: UIAlertController.self) {
print("UIAlertController is presented")}
对于那些不知道如何获得最顶级的人Viewcontroller
extension UIApplication {
public class func topViewController(_ base: UIViewController? = UIApplication.shared.windows.first?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}}
关闭当前控制器并像
一样呈现警报控制器 func alert(_ message:String) {
let alert = UIAlertController(title: "Error!", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
self.dismiss(animated: false, completion: nil)
self.present(alert, animated: true,completion: nil)
}
您可以在一行中测试是否已显示警报:
if self.presentedViewController as? UIAlertController != nil {
print ("alert already presented")
}
我发现我需要创建一个队列来堆叠 UIAlertController 请求。
NSMutableArray *errorMessagesToShow; // in @interface
errorMessagesToShow=[[NSMutableArray alloc] init]; // in init
-(void)showError:(NSString *)theErrorMessage{
if(theErrorMessage.length>0){
[errorMessagesToShow addObject:theErrorMessage];
[self showError1];
}
}
-(void)showError1{
NSString *theErrorMessage;
if([errorMessagesToShow count]==0)return; // queue finished
UIViewController* parentController =[[UIApplication sharedApplication]keyWindow].rootViewController;
while( parentController.presentedViewController &&
parentController != parentController.presentedViewController ){
parentController = parentController.presentedViewController;
}
if([parentController isKindOfClass:[UIAlertController class]])return; // busy
// construct the alert using [errorMessagesToShow objectAtIndex:0]
// add to each UIAlertAction completionHandler [self showError1];
// then
[errorMessagesToShow removeObjectAtIndex:0];
[parentController presentViewController:alert animated:YES completion:nil];
}