UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Hello" message:@"Habr!" preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:@"Action" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { // }]];
このアプローチにより、より構造化された論理コードを作成できます。 これ以降、プログラマはダイアログボックスの作成とイベントの処理を分離する必要がなくなりました-UIAlertControllerはこの誤解を排除しますが、同時にiOS 7以前のバージョンでは使用できないため、歴史的な不正をもたらします。 この問題を解決するにはいくつかの方法があります。
- UIAlertControllerを無視し、廃止されたUIAlertViewとUIActionSheetを引き続き使用します。
- カスタムダイアログを使用します。 プログラマは、時間コストの増加につながる独自の実装を作成するか、サードパーティのコンポーネント(たとえばSIAlertView )を接続しますが、その使用にはいくつかの欠点があります。
- 優れたサポートを備えたソフトウェアモジュールを指で数えることができます(多くの場合、作成者はこの感謝のないタスクをすぐに投げます)
- プロジェクトが異なる開発者の複数のコンポーネントを使用している場合、それらの相互作用が問題を引き起こす可能性があります(まれに、可能です)。
- iOSのバージョンを確認し、UIAlertController、またはUIAlertViewまたはUIActionSheetを作成します。
後者のオプションは最も論理的であり、ほとんどの開発者が選択すると確信していますが、この方法には重大な欠点があります。ダイアログボックスを表示する必要があるたびに、オペレーティングシステムのバージョンをチェックする条件を記述する必要があります。 実際にこれに直面して、特別なラッパークラスUIAlertDialogを作成しました。これにより、この問題を忘れることができます。
そのアイデアは、UIAlertControllerの便利なブロック構文を、iOSの最新バージョンに限定されないプロジェクトで使用できるということです。
ダイアログボックススタイルの定義
typedef NS_ENUM(NSInteger, UIAlertDialogStyle) { UIAlertDialogStyleAlert = 0, UIAlertDialogStyleActionSheet };
ハンドラーブロックの種類
typedef void(^UIAlertDialogHandler)(NSInteger buttonIndex);
クラス構造に移動できます:
@interface UIAlertDialog : NSObject <UIAlertViewDelegate, UIActionSheetDelegate> - (instancetype)initWithStyle:(UIAlertDialogStyle)style title:(NSString *)title andMessage:(NSString *)message; - (void)addButtonWithTitle:(NSString *)title andHandler:(UIAlertDialogHandler)handler; - (void)showInViewController:(UIViewController *)viewContoller; @end
コンストラクター内
- (instancetype)initWithStyle:(UIAlertDialogStyle)style title:(NSString *)title andMessage:(NSString *)message { if (self = [super init]) { self.style = style; self.title = title; self.message = message; self.items = [NSMutableArray new]; } return self; }
転送されたパラメーターは
内部変数
@interface UIAlertDialog () @property (nonatomic) UIAlertDialogStyle style; @property (copy, nonatomic) NSString *title; @property (copy, nonatomic) NSString *message; @property (strong, nonatomic) NSMutableArray *items; @end
そして、ボタンのアクションを保存する配列が初期化されます(アイテム)。
新しいボタンを追加する:
- (void)addButtonWithTitle:(NSString *)title andHandler:(UIAlertDialogHandler)handler { UIAlertDialogItem *item = [UIAlertDialogItem new]; item.title = title; item.handler = handler; [self.items addObject:item]; }
UIAlertDialogItemは
特別な内部クラス(UIAlertActionのアナログ)
@interface UIAlertDialogItem : NSObject @property (copy, nonatomic) NSString *title; @property (copy, nonatomic) UIAlertDialogHandler handler; @end
ボタンのテキストとそれに関連付けられたアクションを保存します。
最後に、オペレーティングシステムのバージョンに応じてダイアログボックスの作成をカプセル化するshowInViewControllerメソッド:
- (void)showInViewController:(UIViewController *)viewContoller { if ([[UIDevice currentDevice].systemVersion intValue] > 7) { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self showAlertControllerInViewController:viewContoller]; }]; return; } if (self.style == UIAlertDialogStyleActionSheet) { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self showActionSheetInView:viewContoller.view]; }]; } else { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self showAlert]; }]; } }
対応する各メソッドはすぐに実行されるのではなく、メインの実行キューに追加されることを強調します。 これは、ボタンハンドラで別のダイアログボックスが作成された場合、前のダイアログのアニメーションが完了した後にのみ表示されるためです。
ダイアログボックスを作成する方法を詳細に検討してください。
UIAlertController
- (void)showAlertControllerInViewController:(UIViewController *)viewController { UIAlertController *alertController = [UIAlertController alertControllerWithTitle:self.title message:self.message preferredStyle:self.style == UIAlertDialogStyleActionSheet ? UIAlertControllerStyleActionSheet : UIAlertControllerStyleAlert]; NSInteger i = 0; for (UIAlertDialogItem *item in self.items) { UIAlertAction *alertAction = [UIAlertAction actionWithTitle:item.title style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { NSInteger buttonIndex = i; if (item.handler) { item.handler(buttonIndex); } }]; [alertController addAction:alertAction]; i++; } UIAlertAction *closeAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"close", nil) style:UIAlertActionStyleCancel handler:nil]; [alertController addAction:closeAction]; [viewController presentViewController:alertController animated:YES completion:nil]; }
このリストでは、行をマークしたいと思います
NSInteger buttonIndex = i;
むしろ、コード内での位置。 作成されたコンテキストを保存するブロックのプロパティにより、押されたボタンのインデックスをブロックハンドラーに転送することが可能になります。 このメソッドは必須です。UIAlertActionには目的のパラメーターが含まれていません。
UIAlertViewおよびUIActionSheet
UIAlertDialogの説明によると、ダイアログボックスの作成は次のようになります。
- (void)showMessage:(NSString *)message { UIAlertDialog *alertDialog = [[UIAlertDialog alloc] initWithStyle:UIAlertDialogStyleAlert title:message andMessage:nil]; [alertDialog showInViewController:self]; }
このクラスはUIAlertViewとUIActionSheetのデリゲートであるという事実のため
@interface UIAlertDialog : NSObject <UIAlertViewDelegate, UIActionSheetDelegate>
1つのポイントを明確にする必要があります。
ご存じのように、クラスのデリゲートは、 weak修飾子を持つプロパティとして記述されます。 これは、デリゲートオブジェクトへの強い参照が存在しない場合、デリゲートメソッドを呼び出そうとするとEXC_BAD_ACCESS例外がスローされることを意味します。
私たちの場合、これはまさに起こることです-ARCは外部へのリンクがないため、 alertDialogを削除します。 この問題は、UIAlertViewおよびUIActionSheetの下位クラスを作成し、それらのダイアログオブジェクトへのリンクを追加することで解決できます。
@interface UIAlertViewDialog : UIAlertView @property (strong, nonatomic) UIAlertDialog *alertDialog; @end
そして
@interface UIActionSheetDialog : UIActionSheet @property (strong, nonatomic) UIAlertDialog *alertDialog; @end
操作のおかげで、ダイアログボックスを作成するためのコードは次の形式になります。
- (void)showActionSheetInView:(UIView *)view { UIActionSheetDialog *actionSheet = [[UIActionSheetDialog alloc] initWithTitle:self.title delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil]; actionSheet.alertDialog = self; for (UIAlertDialogItem *item in self.items) { [actionSheet addButtonWithTitle:item.title]; } [actionSheet addButtonWithTitle:NSLocalizedString(@"close", nil)]; actionSheet.cancelButtonIndex = actionSheet.numberOfButtons - 1; [actionSheet showInView:view.window]; }
UIAlertViewに類似
- (void)showAlert { UIAlertViewDialog *alertView = [[UIAlertViewDialog alloc] initWithTitle:self.title message:self.message delegate:self cancelButtonTitle:nil otherButtonTitles:nil]; alertView.alertDialog = self; for (UIAlertDialogItem *item in self.items) { [alertView addButtonWithTitle:item.title]; } [alertView addButtonWithTitle:NSLocalizedString(@"close", nil)]; alertView.cancelButtonIndex = alertView.numberOfButtons - 1; [alertView show]; }
最後のタッチ-ボタンのアクションの処理は、対応するデリゲートのメソッドで発生します。
- (void)actionSheet:(UIActionSheetDialog *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex == actionSheet.numberOfButtons - 1) { return; } UIAlertDialogItem *item = self.items[buttonIndex]; if (item.handler) { item.handler(buttonIndex); } }
UIAlertViewDelegate
- (void)alertView:(UIAlertViewDialog *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex == alertView.numberOfButtons - 1) { return; } UIAlertDialogItem *item = self.items[buttonIndex]; if (item.handler) { item.handler(buttonIndex); } }
おわりに
その結果、ダイアログボックス( ソースコード )での作業に費やす時間を大幅に削減するシンプルでコンパクトなソリューションが得られます。
ご清聴ありがとうございました!