仅在验证输入后启用 UIAlertController 的 UIAlertAction

Enable UIAlertAction of UIAlertController only after input is validated

我正在使用 UIAlertController 显示一个带有 UITextField 和一个标记为 "Ok" 的 UIAlertAction 按钮的对话框。在 UITextField 中输入多个字符(比如 5 个字符)之前,如何禁用该按钮?

在头文件中添加以下内容属性

@property(nonatomic, strong)UIAlertAction *okAction;   

然后将以下代码复制到 ViewController

viewDidLoad 方法中
self.okAction = [UIAlertAction actionWithTitle:@"OK"
                                         style:UIAlertActionStyleDefault
                                       handler:nil];
self.okAction.enabled = NO;

UIAlertController *controller = [UIAlertController alertControllerWithTitle:nil
                                                                    message:@"Enter your text"
                                                             preferredStyle:UIAlertControllerStyleAlert];

[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {

    textField.delegate = self;
}];

[controller addAction:self.okAction];
[self presentViewController:controller animated:YES completion:nil];

还在您的 Class

中实现以下 UITextField 委托方法
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

    NSString *finalString = [textField.text stringByReplacingCharactersInRange:range withString:string];
   [self.okAction setEnabled:(finalString.length >= 5)];
   return YES;
}

这应该有效

您可以在 UITextField 中添加观察员:

[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
    [textField addTarget:self action:@selector(alertControllerTextFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}

但首先禁用您的按钮:

okAction.enabled = NO;

然后按照您指定的方法进行验证:

- (void)alertTextFieldDidChange:(UITextField *)sender {
  UIAlertController *alertController = (UIAlertController *)self.presentedViewController;
  if (alertController) {
    UITextField *someTextField = alertController.textFields.firstObject;
    UIAlertAction *okAction = alertController.actions.lastObject;
    okAction.enabled = someTextField.text.length > 2;
  }
}

更好的方法是在验证输入后提醒用户他的输入有什么问题,这样用户就知道应用程序对他的期望是什么。

- (void)askReasonWithPreviousReason:(NSString *)text
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:@"Enter reason" preferredStyle:UIAlertControllerStyleAlert];

    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField)
     {
         textField.text = text;
     }];

    [alertController addAction:[UIAlertAction actionWithTitle:@"Save" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action)
                                {
                                    if ([self isReasonValid:alertController.textFields.firstObject.text])
                                    {
                                        UIAlertController *alertController2 = [UIAlertController alertControllerWithTitle:AlertTitle message:@"Are you sure you would like to save?" preferredStyle:UIAlertControllerStyleAlert];

                                        [alertController2 addAction:[UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action)
                                                                     {
                                                                         [self saveReason:alertController.textFields.firstObject.text];
                                                                     }]];
                                        [alertController2 addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:Nil]];

                                        [self presentViewController:alertController2 animated:YES completion:nil];
                                    }
                                }]];

    [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:Nil]];

    [self presentViewController:alertController animated:YES completion:nil];
}

- (BOOL)isReasonValid:(NSString *)reason
{
    NSString *errorMessage = [[NSString alloc] init];

    if (reason.length < 5)
    {
        errorMessage = @"Reason must be more than 5 characters";
    }
    else if (reason.length > 100)
    {
        errorMessage = @"Reason must be less than 100 characters";
    }

    if (errorMessage.length != 0)
    {
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:errorMessage preferredStyle:UIAlertControllerStyleAlert];

        [alertController addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action)
                                    {
                                        [self askReasonWithPreviousReason:reason];
                                    }]];

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

        return NO;
    }

    return YES;
}

Swift 3 实现基于 soulshined 的回答:

var someAlert: UIAlertController {
    let alert = UIAlertController(title: "Some Alert", message: nil, preferredStyle: .alert)

    alert.addTextField {
        [=10=].placeholder = "Write something"
        [=10=].addTarget(self, action: #selector(self.textFieldTextDidChange(_:)), for: .editingChanged)
    }

    alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))

    let submitAction = UIAlertAction(title: "Submit", style: .default) { _ in
        // Do something...
    }

    submitAction.isEnabled = false
    alert.addAction(submitAction)
    return alert
}

func textFieldTextDidChange(_ textField: UITextField) {
    if let alert = presentedViewController as? UIAlertController,
        let action = alert.actions.last,
        let text = textField.text {
        action.isEnabled = text.characters.count > 0
    }
}

我已经 an answer for another post asking basically the same question on Whosebug. To summarize, there are several ways to do this: use UITextFieldDelegate, Notification, KVO, or plainly add event handling target on the control. My solution is a simple UIAlertController subclass 环绕事件处理目标,您只需调用

即可配置
    alert.addTextField(configurationHandler: { (textField) in
        textField.placeholder = "Your name"
        textField.autocapitalizationType = .words
    }) { (textField) in
        saveAction.isEnabled = (textField.text?.characters.count ?? 0) > 0
    }

如果您在项目中必须多次处理此类警报,这应该很方便。

使用 Swift 5.3 和 iOS 14,您可以使用 Combine 框架和 NotificationCenter 跟踪给定 UITextFieldUITextField.textDidChangeNotification 通知。


以下代码显示了一种可能的实现方式,以便根据 textField 的字符数启用 UIAlertController 的按钮:

import UIKit
import Combine

class ViewController: UIViewController {

    var cancellable: AnyCancellable?

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .systemBackground

        let action = UIAction(
            title: "Change title",
            handler: { [unowned self] _ in
                self.presentAlert()
            }
        )
        let barButtonItem = UIBarButtonItem(primaryAction: action)
        navigationItem.rightBarButtonItem = barButtonItem
    }

}
extension ViewController {

    func presentAlert() {
        let alertController = UIAlertController(
            title: "Change title",
            message: nil,
            preferredStyle: .alert
        )
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in
            print("Cancelled")
        }
        let renameAction = UIAlertAction(
            title: "Rename",
            style: .default
        ) { [unowned alertController] action in
            print("Renamed: \(alertController.textFields!.first!.text!)")
        }
        renameAction.isEnabled = false

        alertController.addAction(cancelAction)
        alertController.addAction(renameAction)
        alertController.addTextField(configurationHandler: { textField in
            self.cancellable = NotificationCenter.default
                .publisher(for: UITextField.textDidChangeNotification, object: textField)
                .sink(receiveValue: { _ in
                    let textCount = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines).count ?? 0
                    renameAction.isEnabled = textCount >= 5 // min 5 characters
                })
        })
        present(alertController, animated: true)
    }

}