ios 进入后台状态时关闭 UIAlertViews
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3105974/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Dismissing UIAlertViews when entering background state
提问by Fran?ois P.
Apple recommends dismissing any UIAlertViews/UIActionSheets
when entering background state in iOS 4. This is to avoid any confusion on the user's part when he relaunches the application later. I wonder how I could elegantly dismiss all UIAlertViews at once, without retaining a reference to it everytime I set one up...
Apple 建议UIAlertViews/UIActionSheets
在 iOS 4 中进入后台状态时关闭 any 。这是为了避免用户稍后重新启动应用程序时出现任何混淆。我想知道如何一次优雅地关闭所有 UIAlertViews,而不在每次设置时保留对它的引用......
Any idea ?
任何的想法 ?
采纳答案by Guillaume
I was intrigued by Dad's answer(funny username :), and curious why it was down-voted.
我对爸爸的回答很感兴趣(有趣的用户名:),并好奇为什么它被否决了。
So I tried it.
所以我试了一下。
Here is the .m part of a subclass of UIAlertView.
这是 UIAlertView 子类的 .m 部分。
Edit:(Cédric) I have added a way to catch calls to delegate methods and remove the observer then to avoid multiple registrations to the notification center.
编辑:(Cédric)我添加了一种方法来捕获对委托方法的调用,然后删除观察者以避免多次注册到通知中心。
Everything bundled in a class in this github repo: https://github.com/sdarlington/WSLViewAutoDismiss
一切都捆绑在这个 github 存储库中的一个类中:https: //github.com/sdarlington/WSLViewAutoDismiss
#import "UIAlertViewAutoDismiss.h"
#import <objc/runtime.h>
@interface UIAlertViewAutoDismiss () <UIAlertViewDelegate> {
id<UIAlertViewDelegate> __unsafe_unretained privateDelegate;
}
@end
@implementation UIAlertViewAutoDismiss
- (id)initWithTitle:(NSString *)title
message:(NSString *)message
delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ...
{
self = [super initWithTitle:title
message:message
delegate:self
cancelButtonTitle:cancelButtonTitle
otherButtonTitles:nil, nil];
if (self) {
va_list args;
va_start(args, otherButtonTitles);
for (NSString *anOtherButtonTitle = otherButtonTitles; anOtherButtonTitle != nil; anOtherButtonTitle = va_arg(args, NSString *)) {
[self addButtonWithTitle:anOtherButtonTitle];
}
privateDelegate = delegate;
}
return self;
}
- (void)dealloc
{
privateDelegate = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
[super dealloc];
}
- (void)setDelegate:(id)delegate
{
privateDelegate = delegate;
}
- (id)delegate
{
return privateDelegate;
}
- (void)show
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[super show];
}
- (void)applicationDidEnterBackground:(NSNotification *)notification
{
[super dismissWithClickedButtonIndex:[self cancelButtonIndex] animated:NO];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}
#pragma mark - UIAlertViewDelegate
// The code below avoids to re-implement all protocol methods to forward to the real delegate.
- (id)forwardingTargetForSelector:(SEL)aSelector
{
struct objc_method_description hasMethod = protocol_getMethodDescription(@protocol(UIAlertViewDelegate), aSelector, NO, YES);
if (hasMethod.name != NULL) {
// The method is that of the UIAlertViewDelegate.
if (aSelector == @selector(alertView:didDismissWithButtonIndex:) ||
aSelector == @selector(alertView:clickedButtonAtIndex:))
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}
return privateDelegate;
}
else {
return [super forwardingTargetForSelector:aSelector];
}
}
@end
It works nicely. It's great, because you can just start using it the same way that you used to use UIAlertView.
它工作得很好。这很棒,因为您可以像使用 UIAlertView 一样开始使用它。
I haven't had time to test it thoroughly, but I didn't notice any side effect.
我没有时间彻底测试它,但我没有注意到任何副作用。
回答by Charter
My call would be to add a category to UIAlertview adding the following function :
我的电话是向 UIAlertview 添加一个类别,添加以下功能:
- (void) hide {
[self dismissWithClickedButtonIndex:0 animated:YES];
}
And to suscribe to UIApplicationWillResignActiveNotification
:
并订阅UIApplicationWillResignActiveNotification
:
[[NSNotificationCenter defaultCenter] addObserver:alertView selector:@selector(hide) name:@"UIApplicationWillResignActiveNotification" object:nil];
回答by Wilbert
A totally different approach is a recursive search.
一种完全不同的方法是递归搜索。
Recursive function for your application delegate
应用程序委托的递归函数
- (void)checkViews:(NSArray *)subviews {
Class AVClass = [UIAlertView class];
Class ASClass = [UIActionSheet class];
for (UIView * subview in subviews){
if ([subview isKindOfClass:AVClass]){
[(UIAlertView *)subview dismissWithClickedButtonIndex:[(UIAlertView *)subview cancelButtonIndex] animated:NO];
} else if ([subview isKindOfClass:ASClass]){
[(UIActionSheet *)subview dismissWithClickedButtonIndex:[(UIActionSheet *)subview cancelButtonIndex] animated:NO];
} else {
[self checkViews:subview.subviews];
}
}
}
Calling it from the applicationDidEnterBackground procedure
从 applicationDidEnterBackground 过程调用它
[self checkViews:application.windows];
回答by Dad
huh. Haven't tried this yet, but I wonder if it would make sense to create a subclass of UIAlertView that listens for this Notification and closes itself if so...
呵呵。还没有尝试过这个,但我想知道创建一个 UIAlertView 的子类来监听这个 Notification 并关闭自己是否有意义...
That'd have the "automatically" without retaining / keeping it around characteristic OP is requesting. Make sure to unregister for the notification on close (else boom!)
那将具有“自动”而不保留/保持它围绕 OP 所要求的特征。确保在关闭时取消注册通知(否则繁荣!)
回答by pIkEL
As someone mentioned in a comment: the accepted answer isn't the best/cleanest one since iOS 4.0 when we have blocks! Here's how I do it:
正如有人在评论中提到的那样:当我们有块时,接受的答案并不是自 iOS 4.0 以来最好/最干净的答案!这是我的方法:
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Alert!" message:@"This alert will dismiss when application resigns active!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification){
[alert dismissWithClickedButtonIndex:0 animated:NO];
}];
回答by jcady
UIAlertView was deprecated in iOS 8 in favor of the UIAlertController. Unfortunately, this proves to be a tricky problem because the accepted solution won't work, as Apple explicitly doesn't support subclassing UIAlertController:
UIAlertView 在 iOS 8 中被弃用,取而代之的是 UIAlertController。不幸的是,这被证明是一个棘手的问题,因为公认的解决方案不起作用,因为 Apple 明确不支持子类化 UIAlertController:
The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.
UIAlertController 类旨在按原样使用,不支持子类化。此类的视图层次结构是私有的,不得修改。
My solution is to simply traverse the view controller tree and dismiss all UIAlertControllers that you find. You can enable this globally by creating an extension of UIApplication and then calling it in the AppDelegate applicationDidEnterBackground
method.
我的解决方案是简单地遍历视图控制器树并关闭您找到的所有 UIAlertControllers。您可以通过创建 UIApplication 的扩展然后在 AppDelegateapplicationDidEnterBackground
方法中调用它来全局启用此功能。
Try this (in Swift):
试试这个(在 Swift 中):
extension UIApplication
{
class func dismissOpenAlerts(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController)
{
//If it's an alert, dismiss it
if let alertController = base as? UIAlertController
{
alertController.dismissViewControllerAnimated(false, completion: nil)
}
//Check all children
if base != nil
{
for controller in base!.childViewControllers
{
if let alertController = controller as? UIAlertController
{
alertController.dismissViewControllerAnimated(false, completion: nil)
}
}
}
//Traverse the view controller tree
if let nav = base as? UINavigationController
{
dismissOpenAlerts(nav.visibleViewController)
}
else if let tab = base as? UITabBarController, let selected = tab.selectedViewController
{
dismissOpenAlerts(selected)
}
else if let presented = base?.presentedViewController
{
dismissOpenAlerts(presented)
}
}
}
And then in your AppDelegate:
然后在你的 AppDelegate 中:
func applicationDidEnterBackground(application: UIApplication)
{
UIApplication.dismissOpenAlerts()
}
回答by Ponja
I Have had solved this with the following code:
我已经用以下代码解决了这个问题:
/* taken from the post above (Cédric)*/
- (void)checkViews:(NSArray *)subviews {
Class AVClass = [UIAlertView class];
Class ASClass = [UIActionSheet class];
for (UIView * subview in subviews){
NSLog(@"Class %@", [subview class]);
if ([subview isKindOfClass:AVClass]){
[(UIAlertView *)subview dismissWithClickedButtonIndex:[(UIAlertView *)subview cancelButtonIndex] animated:NO];
} else if ([subview isKindOfClass:ASClass]){
[(UIActionSheet *)subview dismissWithClickedButtonIndex:[(UIActionSheet *)subview cancelButtonIndex] animated:NO];
} else {
[self checkViews:subview.subviews];
}
}
}
/*go to background delegate*/
- (void)applicationDidEnterBackground:(UIApplication *)application
{
for (UIWindow* window in [UIApplication sharedApplication].windows) {
NSArray* subviews = window.subviews;
[self checkViews:subviews];
}
}
回答by GorillaPatch
The straightforward way is to hold a reference to the UIAlertView so you can dismiss it. Of course as petert mentioned you can do it with a Notification or use the delegate method on UIApplication
直接的方法是持有对 UIAlertView 的引用,以便您可以关闭它。当然,正如 petert 提到的,您可以使用 Notification 或在 UIApplication 上使用委托方法
applicationWillResignActive:
does not always mean that you are going to the background. You will for example also receive that delegate call and notification (you get both) when the user gets a phone call or receives and SMS. So you have to decide what should happen if the user gets an SMS and presses cancel to stay in your app. You maybe want to make sure that your UIAlertView is still there.
并不总是意味着你要去后台。例如,当用户接到电话或收到短信时,您还将收到该委托电话和通知(您两者都会收到)。因此,您必须决定如果用户收到 SMS 并按下取消以留在您的应用程序中会发生什么。您可能想确保您的 UIAlertView 仍然存在。
So I would dismiss the UIAlertView and save the state in the delegate call when you really go into the background:
因此,当您真正进入后台时,我会关闭 UIAlertView 并将状态保存在委托调用中:
applicationDidEnterBackground:
Have a look at Session 105 - Adopting Multitasking on iOS4 of WWDC10 available for free at developer.apple.com. It gets interesting at 16:00 min
查看 Session 105 - 在 WWDC10 的 iOS4 上采用多任务处理,可在 developer.apple.com 上免费获取。在 16:00 分变得有趣
Check out this graphicto understand the different states of an application
查看此图形以了解应用程序的不同状态
回答by petert
I have this on my TODO list, but my first instinct would be to listen out for the notifcation UIApplicationWillResignActiveNotification
(see UIApplication) in the views where you have things like UIAlertView - here you can programmatically remove the alert view with:
我的 TODO 列表中有这个,但我的第一直觉是UIApplicationWillResignActiveNotification
在有 UIAlertView 之类的视图中监听通知(请参阅 UIApplication) - 在这里,您可以通过以下方式以编程方式删除警报视图:
(void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
The discussion for this method even suggests what it's for in iOS4!
对这种方法的讨论甚至暗示了它在 iOS4 中的用途!
In iPhone OS 4.0, you may want to call this method whenever your application moves to the background. An alert view is not dismissed automatically when an application moves to the background. This behavior differs from previous versions of the operating system, where they were canceled automatically when the application was terminated. Dismissing the alert view gives your application a chance to save changes or abort the operation and perform any necessary cleanup in case your application is terminated later.
在 iPhone OS 4.0 中,您可能希望在应用程序移至后台时调用此方法。当应用程序移至后台时,警报视图不会自动关闭。这种行为不同于以前版本的操作系统,在应用程序终止时它们会自动取消。关闭警报视图使您的应用程序有机会保存更改或中止操作并执行任何必要的清理,以防您的应用程序稍后终止。
回答by johnbakers
if you only have one or two specific alert windows you show (as do most apps), then you can just create an assign
ivar to the alert:
如果您只显示一两个特定的警报窗口(大多数应用程序也是如此),那么您只需assign
为警报创建一个ivar:
@property (nonatomic, assign) UIAlertView* alertview;
Then, in the app delegate:
然后,在应用程序委托中:
[self.viewController.alertview dismissWithClickedButtonIndex:[self.viewController.alertview cancelButtonIndex] animated:NO];
You can put this in applicationDidEnterBackground:
or wherever you see fit. It closes the alert programmatically upon application exit. I've been doing this and it works great.
你可以把它放在applicationDidEnterBackground:
你认为合适的地方。它在应用程序退出时以编程方式关闭警报。我一直在这样做,而且效果很好。