【发布时间】:2011-02-01 12:05:46
【问题描述】:
我有一个方法可以发布 HTTP 数据并在出现错误时显示 UIAlertView。如果我有多个 HTTP 帖子,我将为每个错误显示多个 UIAlertView。
我只想在不显示其他 UIAlertView 的情况下显示 UIAlertView。 我如何确定这一点?
【问题讨论】:
标签: ios iphone objective-c uialertview
我有一个方法可以发布 HTTP 数据并在出现错误时显示 UIAlertView。如果我有多个 HTTP 帖子,我将为每个错误显示多个 UIAlertView。
我只想在不显示其他 UIAlertView 的情况下显示 UIAlertView。 我如何确定这一点?
【问题讨论】:
标签: ios iphone objective-c uialertview
为什么不只检查由 UIAlertView 类维护的可见属性?
if (_alert) //alert is a retained property
{
self.alert = [[[UIAlertView alloc] initWithTitle:@"Your Title"
message:@"Your message"
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"OK"] autorelease];
}
if (!_alert.visible)
{
[_alert show];
}
【讨论】:
在调用 UIAlertView 上的 show 方法之前调用 set an ivar 的对象。
...
if (!self.alertShowing) {
theAlert = [[UIAlertView alloc] initWithTitle:title message:details delegate:self cancelButtonTitle:nil otherButtonTitles:@"Okay", nil];
self.alertShowing = YES;
[theAlert show];
}
...
然后在警报管理的委托方法中将标志 ivar 设置为 no:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
...
self.alertShowing = NO;
}
如果您希望警报按顺序显示,我会发布通知以将每条消息添加到队列中,然后仅在解除警报后从队列中取出一条消息。
【讨论】:
visible属性,如下面的答案。
如果您可以控制其他警报视图,请检查每个警报视图的 visible 属性。
在 iOS 6 或更早版本中,当警报出现时,它会被移动到 _UIAlertOverlayWindow。因此,一个相当脆弱的方法是遍历所有窗口并检查是否有任何 UIAlertView 子视图。
for (UIWindow* window in [UIApplication sharedApplication].windows) {
NSArray* subviews = window.subviews;
if ([subviews count] > 0)
if ([[subviews objectAtIndex:0] isKindOfClass:[UIAlertView class]])
return YES;
}
return NO;
这是未记录的,因为它取决于内部视图层次结构,尽管 Apple 不能抱怨这一点。一个更可靠但更无证的方法是检查if [_UIAlertManager visibleAlert] is nil。
这些方法无法检查 SpringBoard 中的 UIAlertView 是否显示。
【讨论】:
- (BOOL)checkAlertExist {
for (UIWindow* window in [UIApplication sharedApplication].windows) {
NSArray* subviews = window.subviews;
if ([subviews count] > 0) {
for (id cc in subviews) {
if ([cc isKindOfClass:[UIAlertView class]]) {
return YES;
}
}
}
}
return NO;
}
【讨论】:
另一个适用于整个应用且不涉及遍历视图堆栈的选项是将UIAlertView 子类化为MyUIAlertView,添加静态(类)变量BOOL alertIsShowing,并覆盖-(void)show 选择器。
在覆盖的 show 选择器中,检查 alertIsShowing 变量。如果是YES,则在延迟后重试(使用dispatch_after 或设置NSTimer)。如果是NO,请继续拨打[super show] 并将YES 分配给alertIsShowing;当警报视图被隐藏时,将alertIsShowing 设置回NO(你需要聪明地处理委托)。
最后,检查并用MyUIAlertView替换所有UIAlertView实例。
【讨论】:
UIAlertController 进行了子类化-如果您同时使用两者,我认为您需要同时跟踪两者)。我确实最终使用了计数器而不是布尔值。这样,如果您尝试同时显示两个警报,如果出现/消失方法被调用与您期望的不同步,您就不会意外地将布尔值设置为 false。
斯威夫特:
func showAlert(withTitle title: String, message: String, viewController: UIViewController) {
if viewController.presentedViewController == nil { // Prevent multiple alerts at the same time
let localizedTitle = NSLocalizedString(title, comment: "")
let localizedMessage = NSLocalizedString(message, comment: "")
let alert = UIAlertController(title: localizedTitle, message: localizedMessage, preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default, handler: nil)
alert.addAction(action)
viewController.presentViewController(alert, animated: true, completion: nil)
}
}
【讨论】:
我认为它会起作用:
-(BOOL) doesAlertViewExist {
if ([[UIApplication sharedApplication].keyWindow isMemberOfClass:[UIWindow class]])
{
return NO;//AlertView does not exist on current window
}
return YES;//AlertView exist on current window
}
【讨论】:
-(BOOL) doesAlertViewExist { return ![[UIApplication sharedApplication].keyWindow isMemberOfClass:[UIWindow class]]; }
// initialize default flag for alert... If alert is not open set isOpenAlert as NO
BOOL isAlertOpen;
isAlertOpen = NO;
if (isAlertOpen == NO) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"Alert is Open" delegate:self cancelButtonTitle:@"Okay!!" otherButtonTitles: nil];
[alert show];
// Now set isAlertOpen to YES
isAlertOpen = YES;
}
else
{
//Do something
}
【讨论】:
+ (BOOL)checkAlertExist {
for (UIWindow* window in [UIApplication sharedApplication].windows) {
if ([window.rootViewController.presentedViewController isKindOfClass:[UIAlertController class]]) {
return YES;
}
}
return NO;
}
【讨论】:
关于我在视图层次结构中查找 UIAlertView 的一些注意事项:
我尝试循环遍历所有 [UIApplication sharedApplication].windows 视图,但找不到任何东西。
UIApplication 文档的 windows 属性声明如下:
此属性包含当前关联的 UIWindow 对象 应用程序。 此列表不包括由 系统,例如用于显示状态栏的窗口。
所以这让我意识到UIAlertView 所在的UIWindow 甚至没有呈现给我们。
但是,UIApplication 上还有一个名为 keyWindow 的属性。通过循环,我发现了可以组成警报视图的私有类:
在 iOS 7 上:_UIModalItemHostingWindow、_UIModalItemAlertContentView、_UIBackdropEffectView 等
在 iOS 8 上:_UIAlertControllerActionView、_UIAlertControllerShadowedScrollView、_UIBackdropView 等
我找不到我展示的UIAlertView,而是一堆在内部组成它的类。 所以要回答最初的问题,您可能可以使用 keyWindow 属性并查看您是否注意到这些类,但您的应用可能会因为尝试检查私有类而被拒绝。
对于使用适用于 iOS 8 的较新的UIAlertController 的人,可以使用以下方式获取对它的引用:
[UIApplication sharedApplication].keyWindow.rootViewController.presentedViewController。
【讨论】: