Enable UIAlertAction of UIAlertController only after input

前端 未结 6 1686
感情败类
感情败类 2020-12-16 01:39

I am using a UIAlertController to present a dialog with a UITextField and one UIAlertAction button labeled \"Ok\". How do I disable th

相关标签:
6条回答
  • 2020-12-16 01:42

    With Swift 5.3 and iOS 14, you can use Combine framework and NotificationCenter to track UITextField.textDidChangeNotification notifications for a given UITextField.


    The following code shows a possible implementation in order to enable the button of a UIAlertController according to the character count of its textField:

    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)
        }
    
    }
    
    0 讨论(0)
  • 2020-12-16 01:44

    You can add an observer to your UITextField:

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

    but first disable your button:

    okAction.enabled = NO;
    

    Then validate it in the method you specified :

    - (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;
      }
    }
    
    0 讨论(0)
  • 2020-12-16 01:49

    Swift 3 implementation based on soulshined's answer:

    var someAlert: UIAlertController {
        let alert = UIAlertController(title: "Some Alert", message: nil, preferredStyle: .alert)
    
        alert.addTextField {
            $0.placeholder = "Write something"
            $0.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
        }
    }
    
    0 讨论(0)
  • 2020-12-16 01:49

    I had an answer for another post asking basically the same question on stackoverflow. 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 wrapped around the event handling target that you can configure simply by calling

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

    This should be convenient if you have to deal with such alerts more than a few times in the project.

    0 讨论(0)
  • 2020-12-16 01:54

    A better approach would be to alert the user about what is wrong with his input after validating his input, so that the user knows what the app is expecting from him.

    - (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;
    }
    
    0 讨论(0)
  • 2020-12-16 02:01

    Add following property in your header file

    @property(nonatomic, strong)UIAlertAction *okAction;   
    

    then copy the following code in your viewDidLoad method of your ViewController

    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];
    

    Also implement the following UITextField delegate method in your Class

    - (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;
    }
    

    This should work

    0 讨论(0)
提交回复
热议问题