Apple は、iOS 4 でバックグラウンド状態に入るときに any を無視することを推奨していますUIAlertViews/UIActionSheets
。これは、ユーザーが後でアプリケーションを再起動したときに混乱を避けるためです。UIAlertViews を設定するたびに参照を保持せずに、すべての UIAlertViews を一度にエレガントに閉じるにはどうすればよいでしょうか...
何か案が ?
Apple は、iOS 4 でバックグラウンド状態に入るときに any を無視することを推奨していますUIAlertViews/UIActionSheets
。これは、ユーザーが後でアプリケーションを再起動したときに混乱を避けるためです。UIAlertViews を設定するたびに参照を保持せずに、すべての UIAlertViews を一度にエレガントに閉じるにはどうすればよいでしょうか...
何か案が ?
私の呼び出しは、次の関数を追加してUIAlertviewにカテゴリを追加することです:
- (void) hide {
[self dismissWithClickedButtonIndex:0 animated:YES];
}
そして購読するにはUIApplicationWillResignActiveNotification
:
[[NSNotificationCenter defaultCenter] addObserver:alertView selector:@selector(hide) name:@"UIApplicationWillResignActiveNotification" object:nil];
私はお父さんの答え(面白いユーザー名:)に興味をそそられ、なぜそれが反対票を投じられたのか興味がありました。
だからやってみました。
これは、UIAlertViewのサブクラスの.m部分です。
編集:(セドリック)通知センターへの複数の登録を回避するために、メソッドを委任する呼び出しをキャッチしてオブザーバーを削除する方法を追加しました。
この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
それはうまく機能します。UIAlertViewを使用していたのと同じ方法で使用を開始できるので、すばらしいです。
徹底的にテストする時間がありませんでしたが、副作用はありませんでした。
まったく異なるアプローチは、再帰検索です。
アプリケーションデリゲートの再帰関数
- (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];
}
}
}
applicationDidEnterBackgroundプロシージャから呼び出す
[self checkViews:application.windows];
は。これはまだ試していませんが、この Notification をリッスンし、そうであればそれ自体を閉じる UIAlertView のサブクラスを作成するのが理にかなっているのだろうか...
それは、OPが要求している特性を保持/維持せずに「自動的に」持つでしょう。終了時の通知の登録を必ず解除してください (そうでなければブーム!)
誰かがコメントで述べたように: 受け入れられた回答は、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];
}];
UIAlertView は、UIAlertController を支持して iOS 8 で廃止されました。残念ながら、Apple は UIAlertController のサブクラス化を明示的にサポートしていないため、受け入れられた解決策は機能しないため、これは厄介な問題であることがわかります。
UIAlertController クラスはそのまま使用することを意図しており、サブクラス化はサポートしていません。このクラスのビュー階層は非公開であり、変更してはなりません。
私の解決策は、ビュー コントローラー ツリーをたどって、見つけたすべての UIAlertControllers を閉じることです。これをグローバルに有効にするには、UIApplication の拡張機能を作成し、それを AppDelegateapplicationDidEnterBackground
メソッドで呼び出します。
これを試してください(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)
}
}
}
そして、AppDelegate で:
func applicationDidEnterBackground(application: UIApplication)
{
UIApplication.dismissOpenAlerts()
}
私は次のコードでこれを解決しました:
/* 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];
}
}
簡単な方法は、UIAlertViewへの参照を保持して、それを閉じることができるようにすることです。もちろん、petertが述べたように、Notificationでそれを行うか、UIApplicationでデリゲートメソッドを使用することができます
applicationWillResignActive:
必ずしもバックグラウンドに行くことを意味するわけではありません。たとえば、ユーザーが電話を受信したとき、またはSMSを受信したときに、その代理人の呼び出しと通知(両方を取得)も受信します。したがって、ユーザーがSMSを受信し、キャンセルを押してアプリにとどまる場合にどうなるかを決定する必要があります。UIAlertViewがまだそこにあることを確認したい場合があります。
したがって、実際にバックグラウンドに入るときに、UIAlertViewを閉じて、デリゲート呼び出しに状態を保存します。
applicationDidEnterBackground:
WWDC10のiOS4でのセッション105の採用-developer.apple.comで無料で入手できるマルチタスクをご覧ください。16:00分に面白くなる
この図をチェックして、アプリケーションのさまざまな状態を理解してください
私はこれを TODO リストに持っていますが、私の最初の本能は、UIAlertView のUIApplicationWillResignActiveNotification
ようなものがあるビューで通知 (UIApplication を参照) を聞くことです。ここでは、プログラムで警告ビューを削除できます。
(void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
このメソッドの議論は、iOS4 での目的を示唆しています!
iPhone OS 4.0 では、アプリケーションがバックグラウンドに移行するたびにこのメソッドを呼び出すことができます。アプリケーションがバックグラウンドに移行しても、アラート ビューは自動的に閉じられません。この動作は、アプリケーションの終了時に自動的にキャンセルされていた以前のバージョンのオペレーティング システムとは異なります。アラート ビューを閉じると、アプリケーションが後で終了した場合に備えて、変更を保存するか、操作を中止して必要なクリーンアップを実行する機会がアプリケーションに与えられます。
表示する特定のアラート ウィンドウが 1 つまたは 2 つしかない場合 (ほとんどのアプリがそうであるように) assign
、アラートに ivar を作成するだけです。
@property (nonatomic, assign) UIAlertView* alertview;
次に、アプリのデリゲートで:
[self.viewController.alertview dismissWithClickedButtonIndex:[self.viewController.alertview cancelButtonIndex] animated:NO];
これを入れapplicationDidEnterBackground:
たり、好きな場所に置いたりできます。アプリケーションの終了時にプログラムでアラートを閉じます。私はこれを行ってきましたが、うまくいきます。
UIAlert ビューでカテゴリを作成する
http://nshipster.com/method-swizzling/ Swizzle "show" メソッドを使用
週の参照を配列に保持することで、表示されるアラート ビューを追跡します。
- すべてのデータを削除したい場合は、保存されたアラート ビューで Dismiss を呼び出し、配列を空にします。
アプリがバックグラウンドに置かれたときにオブザーバーが削除される、 plkEL のanswerに基づく代替ソリューション。ユーザーがボタンを押してアラートを解除した場合、オブザーバーは引き続きアクティブになりますが、アプリがバックグラウンドに置かれるまでのみです (ブロックが実行され、"nil alertView" で - オブザーバーが削除されます)。
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:alertDelegate
cancelButtonTitle:cancelButtonText
otherButtonTitles:okButtonText, nil];
[alert show];
__weak UIAlertView *weakAlert = alert;
__block __weak id observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue: [NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification){
[weakAlert dismissWithClickedButtonIndex:[weakAlert cancelButtonIndex] animated:NO];
[[NSNotificationCenter defaultCenter] removeObserver:observer];
observer = nil;
}];