UIAlertView が iOS8 で DEPRECATED されて、代わりに UIAlertController を利用する方法が推奨された。UIAlertView では iOS5 から UITextField を使った文字入力が可能になっていた。UIAlertController でも同様の機能は提供されている。
UIAlertController ではボタン押下時のアクションハンドラが delegate から Blocks を使った方法に変わった。そのため、UITextField の enabled プロパティに簡単にアクセスする手段がなくなった。UIAlertView では、文字入力があるまで「OK」などのボタンを押せなくできていたが、UIAlertController ではできなくなった。(不可能と言うよりは、簡単ではなくなった。)
以下、上記の問題を解決するために調査した内容を元に、汎用的に書き換えたソースコードの定型文を示す。自作のプログラムにそのまま取り込んで、文字列リテラルやボタン押下時などの必要な箇所を変更すれば、すぐに利用できるだろう。
-(void)prompt
{
__weak typeof(self) weakSelf = self;
@autoreleasepool {
UIAlertController * alertController =
[UIAlertController alertControllerWithTitle:@"新規フォルダ"
message:@"フォルダ名を入力して下さい"
preferredStyle:UIAlertControllerStyleAlert];
void (^configurationHandler)(UITextField *) = ^(UITextField * textField) {
textField.placeholder = @"フォルダ名";
// ここで UITextField の text が変更したときの通知を受信する設定を実施
[[NSNotificationCenter defaultCenter]
addObserver:weakSelf
selector:@selector(alertTextFieldDidChange:)
name:UITextFieldTextDidChangeNotification
object:textField];
};
[alertController addTextFieldWithConfigurationHandler:configurationHandler];
void (^cancelHandler)(UIAlertAction *) = ^(UIAlertAction * action) {
// UITextField 変更通知は不要
[[NSNotificationCenter defaultCenter]
removeObserver:weakSelf
name:UITextFieldTextDidChangeNotification
object:nil];
};
UIAlertAction * cancelAction =
[UIAlertAction actionWithTitle:@"中止"
style:UIAlertActionStyleCancel
handler:cancelHandler];
[alertController addAction:cancelAction];
void (^createHandler)(UIAlertAction *) = ^(UIAlertAction * action) {
UITextField * textField = alertController.textFields[0];
if (textField.text.length > 0) {
dispatch_block_t mainBlock = ^{
// ここに UI の更新やデータベースへの追記などを実施するコードを記述する
};
dispatch_async(dispatch_get_main_queue(), mainBlock);
}
// 通知はもう要らない
[[NSNotificationCenter defaultCenter]
removeObserver:weakSelf
name:UITextFieldTextDidChangeNotification
object:nil];
};
UIAlertAction * createAction =
[UIAlertAction actionWithTitle:@"作成"
style:UIAlertActionStyleDefault
handler:createHandler];
createAction.enabled = NO; // 最初は「作成」ボタンは押せない
[alertController addAction:createAction];
[self presentViewController:alertController animated:YES completion:nil];
}
}
#pragma mark - NSNotification handler
// ここで UITextField の入力処理を行う
-(void)alertTextFieldDidChange:(NSNotification *)notification
{
UIAlertController * alertController = (UIAlertController *)self.presentedViewController;
if (alertController) {
UITextField * textField = alertController.textFields.firstObject;
UIAlertAction * createAction = alertController.actions.lastObject;
// 文字入力があればボタンを押せるようにする。
// ここの値を 0 から 8 などにすれば、8文字以上の入力がないと有効にならないようにもできる。
createAction.enabled = textField.text.length > 0;
}
}
ソースコードの利用は、上記をそのまま自作のプログラムにコピーした場合は、次の一文で呼び出せる。
[self prompt];
上記コードの NSNotification ハンドラの alertTextFieldDidChange 内の UITextFiled に対する条件処理を変更すれば、n文字以上m文字未満の場合など、簡単に応用可能である。以下は、よくあるパスワード入力の場合の修正箇所のコード。
void (^configurationHandler)(UITextField *) = ^(UITextField * textField) {
textField.placeholder = @"";
textField.secureTextEntry = YES; // 入力文字列を「*」でマスクする(追加部分!)
[[NSNotificationCenter defaultCenter]
addObserver:weakSelf
selector:@selector(alertTextFieldDidChange:)
name:UITextFieldTextDidChangeNotification
object:textField];
};
#pragma mark - NSNotification handler
-(void)alertTextFieldDidChange:(NSNotification *)notification
{
UIAlertController * alertController = (UIAlertController *)self.presentedViewController;
if (alertController) {
UITextField * textField = alertController.textFields.firstObject;
UIAlertAction * createAction = alertController.actions.lastObject;
// パスワードは 10文字以上の入力を必須とする。
createAction.enabled = textField.text.length > 10; // (修正箇所!)
}
}
定型文を使いまわせば、お手軽に応用ができるね。