【Objective-C】どのクラスからでも現在表示されているUIAlertViewを削除する方法【Xcode10.2対応】

こういう人に向けて発信しています。
・レガシーなUIAlerViewの管理をしたい人
・UIAlertViewが開かれているかどうかチェックしたい人
・Objective-C中級者

UIAlertViewとUIAlertControllerの表示の違いについて

UIAlertView *alert =
 [[UIAlertView alloc] initWithTitle:@"お知らせ" message:@"完了しました"
   delegate:self cancelButtonTitle:@"確認" otherButtonTitles:nil];
[alert show];
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"UIAlertControllerStyle.Alert" message:@"iOS8" preferredStyle:UIAlertControllerStyleAlert];
   
   // addActionした順に左から右にボタンが配置されます
   [alertController addAction:[UIAlertAction actionWithTitle:@"はい"
                                                       style:UIAlertActionStyleDefault
                                                     handler:^(UIAlertAction *action) {
                                                         // otherボタンが押された時の処理
                                                     }]];
   
   [alertController addAction:[UIAlertAction actionWithTitle:@"いいえ"
                                                       style:UIAlertActionStyleDefault
                                                     handler:^(UIAlertAction *action) {
                                                         // cancelボタンが押された時の処理
                                                     }]];
   
   [self presentViewController:alertController animated:YES completion:nil];

UIAlertView(レガシー)ではshowとしていますが、
UIAlertViewController(モダン)はpresentViewControllerしていますね。

後者は自身の上に表示するという方法なので、
Viewがぶらさがっているかのチェックでいけますが、
今回取り扱うUIAlertViewに関しては表示チェックが出来なかったです。
(UIViewを検知する方法が出来なかった)

どうやって実現するのか

(1)UIAlertViewを継承したCustomAlertViewを採用。
(2)showメソッドをオーバーライドして、AppDelegate内にインスタンスを保存しておく。
(3)特定処理を行うときにAppDelegateを参照し、インスタンスが存在すれば、アラームを非表示にして、インスタンスをnilにする。

※NSObjectを継承したシングルトンクラスではなぜかhファイルで宣言できず、AppDelegateで実現することに。

クラス構成

・カスタムクラス
・AppDelegate
・ViewController

CustomAlertView.h/m

 //hファイル
#import <UIKit/UIKit.h>

@interface CustomAlertView : UIAlertView
- (void)show;

@end




  //mファイル
#import "CustomAlertView.h"
#import "AppDelegate.h"

@implementation CustomAlertView

//オーバーライズしている
- (void)show{
   if (self) {
       AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
       appDelegate.showAlertView = self;
   }
   [super show];
}

@end

AppDelegate.m

#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
   // Override point for customization after application launch.
   return YES;
}

//記述しました。
- (void)applicationWillResignActive:(UIApplication *)application {
   NSLog(@"applicationWillResignActive");
   if(self.showAlertView){
       [self.showAlertView dismissWithClickedButtonIndex:-1 animated:NO];
       self.showAlertView = nil;
   }
}


- (void)applicationDidEnterBackground:(UIApplication *)application {
   // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
   // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
   // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
   NSLog(@"applicationDidEnterBackground");

}


- (void)applicationDidBecomeActive:(UIApplication *)application {
   // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}


- (void)applicationWillTerminate:(UIApplication *)application {
   // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}


@end

ViewController.h/m

 //hファイル
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
@property (nonatomic) UIAlertView *alert;


@end



 //mファイル
#import "ViewController.h"
#import "AppDelegate.h"
#import "CustomAlertView.h"

@interface ViewController ()

@end

@implementation ViewController

//testDemo
- (void)viewDidLoad {
   [super viewDidLoad];
}

- (IBAction)buttonTapEvent:(UIButton *)sender {
   //UIAlertViewはレガシーな書き方なので
   CustomAlertView *alert = [[CustomAlertView alloc] init];
   alert.title = @"お知らせ";
   alert.message = @"完了しました";
   [alert addButtonWithTitle:@"確認"];
   [alert show];
   
}

@end

なぜshowメソッドオーバーライドしているのか

このメソッドってotherButtonTitles部分って数が決まっていないんですよね。
なので3件とかボタン追加する場合なども対応させるのは億劫なので、
どの場合でも絶対通る showをオーバーライドしました。

懸念:通常にボタンを押下して画面から消えた場合の処理

ボタン押下時のデリゲートメソッドでAppDelegateのインスタンスリセットなどしていませんが、
実際にAppDelegate内のインスタンスチェックの所で処理が行われても落ちることはありませんでした。

インスタンスの対象が既に非表示などになっている場合などは
処理が特に行われないみたいです。

いいなと思ったら応援しよう!