LoginSignup
1
2

More than 5 years have passed since last update.

UIAlertController の UIAlertAction 実行時に、UIAlertView っぽくデリゲートメソッドを呼び出す方法

Last updated at Posted at 2017-11-10

概要

UIAlertView を UIAlertController に置き換えることが決まって(いまさらとか言わないで…)、いろいろと検討した結果、UIAlertView のデリゲートメソッド内の処理をそのままか、ほぼ手を入れずにいきたいという要望があったため、UIAlertView っぽく UIAlertController の UIAlertAction 実行時にデリゲートメソッドを呼び出す実装を考えてみた。

環境

  • MacOS 10.12.6
  • Xcode 9.1

想定

UIAlertView にタグがついてて、デリゲートメソッド内でどのボタンが押されたかを判別して処理を切り分けているよくある感じのコードの UIAlertController への置き換えを想定。

仕様

  • UIAlertView に設定していた tag を UIAlertController に付け替えできるように、UIAlertController に tag を保持できるようにしている
  • UIAlertView に設定していた delegate を UIAlertController に付け替えできるように、UIAlertController に delegate を保持できるようにしている
    • delegate 用に protocol を定義
  • UIAlertView のどのボタンがクリックされたという情報を付け替えるために UIAlertAction を追加する際に index を渡すようにして、UIAlertAction 実行時に index を取得できるようにしている

ソースコード

Gist にアップしました。
https://gist.github.com/macneko-ayu/3bcd1694c47c150ea0e36de4b8a18da4

Swift と Objective-C が混在しているという要件があるので、両方から使えるようにした。
Swift のみでの使用なら、tag と delegate は Computed Property で問題なし。

UIAlertControllerExtension.swift
import UIKit

@objc protocol AlertControllerDelegate: class {
    func alertController(_ alert: UIAlertController, tappedIndex: Int) -> Void
}

private var DelegateKey: UInt8 = 0
private var TagKey: UInt8 = 0

extension UIAlertController {
    func setDelegate(_ delegate: AlertControllerDelegate?) {
        objc_setAssociatedObject(self, &DelegateKey, delegate, .OBJC_ASSOCIATION_RETAIN)
    }

    func getDelegate() -> AlertControllerDelegate? {
        guard let object = objc_getAssociatedObject(self, &DelegateKey) as? AlertControllerDelegate else {
            return nil
        }
        return object
    }

    func setTag(_ tag: Int) {
        objc_setAssociatedObject(self, &TagKey, tag, .OBJC_ASSOCIATION_RETAIN)
    }

    func getTag() -> Int {
        guard let object = objc_getAssociatedObject(self, &TagKey) as? Int else {
            return 0
        }
        return object
    }

    func addAction(title: String, style: UIAlertActionStyle = .default, index: Int) {
        addAction(UIAlertAction(title: title, style: style, handler: { (action) in
            self.getDelegate()?.alertController(self, tappedIndex: index)
            self.setDelegate(nil)
        }))
    }
}
ViewController.m
@interface ViewController () <AlertControllerDelegate> 
@end

@implementation ViewController 

- (void)viewDidLoad {
    [super viewDidLoad];
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"title" message:@"message" preferredStyle:UIAlertControllerStyleAlert];
    [alert setDelegate:self];
    [alert setTag:3];
    [alert addActionWithTitle:@"OK" style:UIAlertActionStyleDefault index:5];
    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
}

- (void)alertController:(UIAlertController * _Nonnull)alert tappedIndex:(NSInteger)tappedIndex {
    if ([alert getTag] == 3 && tappedIndex == 5) {
        NSLog(@"fuga");
    }
}

@end
ViewController.swift
class ViewController: UIViewController, AlertControllerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        let alert = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
        alert.setDelegate(self)
        alert.setTag(5)
        alert.addAction(title: "OK", index: 3)
        present(alert, animated: true, completion: nil)
    }

    func alertController(_ alert: UIAlertController, tappedIndex: Int) {
        if alert.getTag() == 5 && tappedIndex == 3 {
            print("hoge")
        }
    }
}

使い方

ソースコード参照。

まとめ

黒魔術感が強すぎて、うーん…。

1
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2