[material-components-ios][MDCAlertController] Key-Value Codingでアラートのボタンの色を個別に設定する

最近、material-components-iosを触っています。本記事ではDialogsコンポーネントを使ったアラートのボタンの色をボタン毎に変える方法をご紹介します。
2018.08.24

最近、material-components-ios を触ってます

最近マテリアルデザインライクなiOSアプリケーションを作る機会があり、material-components-iosを触っています。

Material Componentsの概要や導入方法については下記記事を参照してください。

[iOS] Material Designの適用をサポートしてくれるライブラリ「Material Components」を試してみた

実際に使おうとすると細かい部分を変えたくなってくる

material-components-ios 自体はマテリアルデザインのコンセプトに忠実に作成されていると思いますが、 実際にアプリに活用する際には「ここだけちょっとマテリアルデザインから外したデザインにしたい」という場面が出てきます。

実際に筆者が経験した要件だと、Dialogsコンポーネントを使って表示した「アラートのボタンの色をボタン毎に変えたい」というものでした。そこで、本記事ではアラートのボタンの色をボタン毎に変える方法をご紹介します。

検証環境

本エントリは以下の環境で検証を行っています。

  • macOS High Sierra バージョン 10.13.6
  • Xcode Version 9.4.1 (9F2000)
  • Apple Swift version 4.1.2 (swiftlang-902.0.54 clang-902.0.39.2)
  • CocoaPods 1.5.3
  • MaterialComponents (60.1.0)

MDCAlertControllerはボタンの色は一括でしか指定できない

DialogsコンポーネントにはMDCAlertControllerというクラスがあり、UIAlertControllerと同じような感覚で使うことができます。

以下はOKとキャンセルボタンを持つアラートを表示するサンプルコードです。
ここで注目していただきたいのがbuttonTitleColorプロパティでボタンの色を設定している部分です。
このようにbuttonTitleColorではボタンの色が全て同じ色になります。

let alertController = MDCAlertController(title: "タイトル",
                                         message: "メッセージ")

alertController.titleColor = .black
alertController.messageColor = .darkGray

// OKボタン
let okAction = MDCAlertAction(title: "OK") { _  in
    // ボタンタップ時の処理
}
alertController.addAction(okAction)

// キャンセルボタン
let cancelAction = MDCAlertAction(title: "キャンセル")
alertController.addAction(cancelAction)

// ボタンの色は一括でしか指定できない!
alertController.buttonTitleColor = .blue

present(alertController, animated: true)

キャンセルボタンの色をOKボタンの色と違う色にしたい

さて、ここで誰かから「キャンセルとOKは意味合いが異なるからボタンの色を変えたい」と言われたとします。
そこでやることはまず、ボタンの色を個別に設定するプロパティなりメソッドが該当のコンポーネントで提供されているかどうかでしょう。
しかしそのような都合の良いI/Fは備わっていません。そのような場合どうしますか?Material Componentsを使うことは諦めてカスタムのViewを作るのも1つの手だと思います。しかし一方で「ボタンの色を変えたいだけなのにカスタムViewを作るのは面倒...」と思う人もいるでしょう。なんとかしてできないか...そこで使うのがKey-Value Codingです。以下のソースコードを見てください。

// キャンセルボタンの色を変える
let actionButtons = alertController.view.value(forKey: "actionButtons") as? [UIButton]
let cancelButton = actionButtons?.first { $0.title(for: .normal) == "キャンセル" }
cancelButton?.setTitleColor(.darkGray, for: .normal)

実行結果は以下です。

MDCAlertControllerの実装を見ると、viewプロパティの実体はMDCAlertControllerViewというクラスでした。
そして、PrivateなプロパティですがMDCAlertControllerViewactionButtonsというプロパティでボタンのArrayを保持していました。
そこで、value(forKey:)メソッドを使ってプロパティ名を指定してUIButtonのArrayを取得し、そのタイトルからキャンセルボタンを取得して色を設定しています。

おわりに

今回はKey-Value Codingでアラートのボタンの色を個別に設定する方法をご紹介しました。 注意点として、プロパティ名を指定して対象のオブジェクトを取得しているので、ライブラリのアップデートによりある日突然動作しなくなる恐れはあります。 実装的にはよろしいとは言えないものの、困った時の解決策の一つとしてこういう方法もあるということは知っておいて損はないかと思います。

また、Key-Value Codingについてもっと詳しく知りたいという方はKey-Value Coding Programming Guide をご覧ください。