シナリオ: ユーザーがビュー コントローラーのボタンをタップします。ビュー コントローラーは (当然ですが) ナビゲーション スタックの最上位にあります。タップすると、別のクラスで呼び出されるユーティリティ クラス メソッドが呼び出されます。そこで問題が発生したため、制御がビュー コントローラーに戻る前に、その場でアラートを表示したいと考えています。
+ (void)myUtilityMethod {
// do stuff
// something bad happened, display an alert.
}
これは可能でしたUIAlertView
(ただし、おそらく完全に適切ではありませんでした)。
この場合、UIAlertController
を の中にどのように表示すればよいでしょうかmyUtilityMethod
?
ベストアンサー1
WWDC で、私はラボの 1 つに立ち寄って、Apple のエンジニアに同じ質問をしました。「 を表示するためのベスト プラクティスは何ですかUIAlertController
?」 すると、この質問はよく寄せられるから、この件についてセッションを開くべきだと冗談を言ったそうです。Apple 社内ではUIWindow
透明な を作成しUIViewController
、その上に を表示していると彼は言いましたUIAlertController
。基本的には、Dylan Betterman の回答と同じです。
しかし、 のサブクラスは使いたくありませんでしたUIAlertController
。そうすると、アプリ全体のコードを変更する必要が出てくるからです。そこで、関連オブジェクトの助けを借りて、 Objective-C でメソッドUIAlertController
を提供するのカテゴリを作成しましたshow
。
関連するコードは次のとおりです。
#import "UIAlertController+Window.h"
#import <objc/runtime.h>
@interface UIAlertController (Window)
- (void)show;
- (void)show:(BOOL)animated;
@end
@interface UIAlertController (Private)
@property (nonatomic, strong) UIWindow *alertWindow;
@end
@implementation UIAlertController (Private)
@dynamic alertWindow;
- (void)setAlertWindow:(UIWindow *)alertWindow {
objc_setAssociatedObject(self, @selector(alertWindow), alertWindow, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIWindow *)alertWindow {
return objc_getAssociatedObject(self, @selector(alertWindow));
}
@end
@implementation UIAlertController (Window)
- (void)show {
[self show:YES];
}
- (void)show:(BOOL)animated {
self.alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.alertWindow.rootViewController = [[UIViewController alloc] init];
id<UIApplicationDelegate> delegate = [UIApplication sharedApplication].delegate;
// Applications that does not load with UIMainStoryboardFile might not have a window property:
if ([delegate respondsToSelector:@selector(window)]) {
// we inherit the main window's tintColor
self.alertWindow.tintColor = delegate.window.tintColor;
}
// window level is above the top window (this makes the alert, if it's a sheet, show over the keyboard)
UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
self.alertWindow.windowLevel = topWindow.windowLevel + 1;
[self.alertWindow makeKeyAndVisible];
[self.alertWindow.rootViewController presentViewController:self animated:animated completion:nil];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// precaution to ensure window gets destroyed
self.alertWindow.hidden = YES;
self.alertWindow = nil;
}
@end
使用例は次のとおりです。
// need local variable for TextField to prevent retain cycle of Alert otherwise UIWindow
// would not disappear after the Alert was dismissed
__block UITextField *localTextField;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Global Alert" message:@"Enter some text" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSLog(@"do something with text:%@", localTextField.text);
// do NOT use alert.textfields or otherwise reference the alert in the block. Will cause retain cycle
}]];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
localTextField = textField;
}];
[alert show];
作成されたはが解放UIWindow
されると破棄されますUIAlertController
。これは が を保持している唯一のオブジェクトだからですUIWindow
。ただし、 をUIAlertController
プロパティに割り当てたり、アクション ブロックの 1 つでアラートにアクセスして の保持カウントを増やしたりすると、 はUIWindow
画面に表示されたままになり、UI がロックされます。 にアクセスする必要がある場合は、上記の使用例コードを参照して回避してくださいUITextField
。
テスト プロジェクトを含む GitHub リポジトリを作成しました。FFグローバルアラートコントローラ