文系プログラマの勉強ノート

スマホアプリ開発やデザインなどについて勉強したことをまとめています

【Xcode】UIAlertController呼び出しを簡略化する

UIAlertControllerを使って一般的なOK、Cancelボタン付きのアラートを表示するには
次のようにコードを書きます。

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"title"
                                                               message:@"message"
                                                        preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK"
                                                   style:UIAlertActionStyleDefault
                                                 handler:^(UIAlertAction *action){
                                                     NSLog(@"Ok is pressed.");
                                                 }];
[alert addAction:okAction];

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"
                                                       style:UIAlertActionStyleCancel
                                                     handler:^(UIAlertAction *action){
                                                         NSLog(@"Cancel is pressed.");
                                                     }];
[alert addAction:cancelAction];

[self presentViewController:alert animated:YES completion:nil];

…長い。
OK、Cancelなどのお決まりの文字列を定数として定義する手間も加えると
ただ短いメッセージを出すだけなのに結構面倒くさいです。

そこで、UIAlertController呼び出しを簡略化するUtilAlertクラスを作りました。

UtilAlert.h

#import <UIKit/UIKit.h>

@interface UtilAlert : NSObject

// OKボタンを持つAlertを表示
+ (void)showAlertOkWithTitle:(NSString *)title message:(NSString *)message okHandler:(void (^)(UIAlertAction *action))okHandler;

// OK、Cancelボタンを持つAlertを表示
+ (void)showAlertOkCancelWithTitle:(NSString *)title message:(NSString *)message okHandler:(void (^)(UIAlertAction *action))okHandler cancelHandler:(void (^)(UIAlertAction *action))cancelHandler;

@end

UtilAlert.m

#import "UtilAlert.h"

static NSString *const AlertButtonOk = @"OK";
static NSString *const AlertButtonCancel = @"Cancel";

@implementation UtilAlert

#pragma mark - Private Methods

/**
 *  rootViewControllerを取得
 *
 *  @return rootViewControllerインスタンス
 */
+ (UIViewController *)rootViewController {
    UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (root.presentedViewController != nil && !root.presentedViewController.isBeingDismissed) {
        root = root.presentedViewController;
    }
    return root;
}

#pragma mark - Public Methods

/**
 *  OKボタンを持つAlertを表示
 *
 *  @param title タイトル
 *  @param message 本文
 *  @param okHandler OKボタンタップ時の処理
 *
 */
+ (void)showAlertOkWithTitle:(NSString *)title message:(NSString *)message okHandler:(void (^)(UIAlertAction *action))okHandler {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title
                                                                   message:message
                                                            preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *okAction = [UIAlertAction actionWithTitle:AlertButtonOk
                                                     style:UIAlertActionStyleDefault
                                                   handler:okHandler];
    [alert addAction:okAction];
    
    [[UtilAlert rootViewController] presentViewController:alert animated:YES completion:nil];
}

/**
 *  OK、Cancelボタンを持つAlertを表示
 *
 *  @param title タイトル
 *  @param message 本文
 *  @param okHandler OKボタンタップ時の処理
 *  @param cancelHandler Cancelボタンタップ時の処理
 *
 */
+ (void)showAlertOkCancelWithTitle:(NSString *)title message:(NSString *)message okHandler:(void (^)(UIAlertAction *action))okHandler cancelHandler:(void (^)(UIAlertAction *action))cancelHandler {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title
                                                                   message:message
                                                            preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *okAction = [UIAlertAction actionWithTitle:AlertButtonOk
                                                       style:UIAlertActionStyleDefault
                                                     handler:okHandler];
    [alert addAction:okAction];
    
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:AlertButtonCancel
                                                       style:UIAlertActionStyleCancel
                                                     handler:cancelHandler];
    [alert addAction:cancelAction];
    
    [[UtilAlert rootViewController] presentViewController:alert animated:YES completion:nil];
}

このUtilAlertを使って冒頭の処理を書き換えると以下のようになります。

    [UtilAlert showAlertOkCancelWithTitle:@"title"
                                  message:@"message"
                                okHandler:^(UIAlertAction *action){
                                    NSLog(@"Ok is pressed.");
                                }
                            cancelHandler:^(UIAlertAction *action){
                                NSLog(@"Cancel is pressed.");
                            }];

コード量が半分に減りました。

解説

presentViewController: はUIViewController派生クラスからしか呼ぶことができません。
なので、[UtilAlert rootViewController] メソッドを作り、ルートとなるViewControllerを取得しています。
これにより、例えば通信クラスなど画面ではない処理からアラートを出すこともできます。