web-dev-qa-db-fra.com

Empêcher le licenciement de UIAlertController

J'ajoute un UITextField à un UIAlertController, qui apparaît comme un AlertView. Avant de rejeter le UIAlertController, je veux valider l'entrée du UITextField. Sur la base de la validation, je souhaite ignorer le UIAlertController ou non. Mais je ne sais pas comment empêcher l'action de rejet de UIAlertController lorsqu'un bouton est enfoncé. Quelqu'un a-t-il résolu ce problème ou des idées par où commencer? Je suis allé sur Google mais pas de chance:/Merci!

49
jona jürgen

Vous avez raison: si l'utilisateur peut appuyer sur un bouton de votre alerte, l'alerte sera rejetée. Vous voulez donc empêcher l'utilisateur d'appuyer sur le bouton! Il s'agit simplement de désactiver vos boutons UIAlertAction. Si une action d'alerte est désactivée, l'utilisateur ne peut pas la toucher pour la rejeter.

Pour combiner cela avec la validation de champ de texte, utilisez une méthode déléguée de champ de texte ou une méthode d'action (configurée dans le gestionnaire de configuration du champ de texte lorsque vous le créez) pour activer/désactiver les UIAlertActions de manière appropriée en fonction du texte qui a (ou n'a pas) été entré .

Voici un exemple. Nous avons créé le champ de texte comme ceci:

alert.addTextFieldWithConfigurationHandler {
    (tf:UITextField!) in
    tf.addTarget(self, action: "textChanged:", forControlEvents: .EditingChanged)
}

Nous avons une action Annuler et une action OK, et nous avons mis l'action OK dans le monde désactivé:

(alert.actions[1] as UIAlertAction).enabled = false

Par la suite, l'utilisateur ne peut pas appuyer sur OK sauf s'il y a du texte dans le champ de texte:

func textChanged(sender:AnyObject) {
    let tf = sender as UITextField
    var resp : UIResponder = tf
    while !(resp is UIAlertController) { resp = resp.nextResponder() }
    let alert = resp as UIAlertController
    (alert.actions[1] as UIAlertAction).enabled = (tf.text != "")
}

EDIT Voici la version actuelle (Swift 3.0.1 et ultérieure) du code ci-dessus:

alert.addTextField { tf in
    tf.addTarget(self, action: #selector(self.textChanged), for: .editingChanged)
}

et

alert.actions[1].isEnabled = false

et

@objc func textChanged(_ sender: Any) {
    let tf = sender as! UITextField
    var resp : UIResponder! = tf
    while !(resp is UIAlertController) { resp = resp.next }
    let alert = resp as! UIAlertController
    alert.actions[1].isEnabled = (tf.text != "")
}
68
matt

J'ai simplifié la réponse de matt sans traverser la hiérarchie de vues. Cela tient plutôt l'action elle-même comme une variable faible. Ceci est un exemple pleinement fonctionnel:

weak var actionToEnable : UIAlertAction?

func showAlert()
{
    let titleStr = "title"
    let messageStr = "message"

    let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.Alert)

    let placeholderStr =  "placeholder"

    alert.addTextFieldWithConfigurationHandler({(textField: UITextField) in
        textField.placeholder = placeholderStr
        textField.addTarget(self, action: "textChanged:", forControlEvents: .EditingChanged)
    })

    let cancel = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: { (_) -> Void in

    })

    let action = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: { (_) -> Void in
        let textfield = alert.textFields!.first!

        //Do what you want with the textfield!
    })

    alert.addAction(cancel)
    alert.addAction(action)

    self.actionToEnable = action
    action.enabled = false
    self.presentViewController(alert, animated: true, completion: nil)
}

func textChanged(sender:UITextField) {
    self.actionToEnable?.enabled = (sender.text! == "Validation")
}
13
ullstrm

Criboter la réponse de @ Matt, voici comment j'ai fait la même chose dans Obj-C

- (BOOL)textField: (UITextField*) textField shouldChangeCharactersInRange: (NSRange) range replacementString: (NSString*)string
{
    NSString *newString = [textField.text stringByReplacingCharactersInRange: range withString: string];

    // check string length
    NSInteger newLength = [newString length];
    BOOL okToChange = (newLength <= 16);    // don't allow names longer than this

    if (okToChange)
    {
        // Find our Ok button
        UIResponder *responder = textField;
        Class uiacClass = [UIAlertController class];
        while (![responder isKindOfClass: uiacClass])
        {
            responder = [responder nextResponder];
        }
        UIAlertController *alert = (UIAlertController*) responder;
        UIAlertAction *okAction  = [alert.actions objectAtIndex: 0];

        // Dis/enable Ok button based on same-name
        BOOL duplicateName = NO;
        // <check for duplicates, here>

        okAction.enabled = !duplicateName;
    }


    return (okToChange);
}
6
Olie

Je me rends compte que c'est dans Objectiv-C mais cela montre le principal. Je mettrai à jour cela avec une version Swift plus tard.

Vous pouvez également faire de même en utilisant un bloc comme cible.

Ajoutez une propriété à votre ViewController pour que le bloc (fermeture pour Swift) ait une référence forte

@property (strong, nonatomic) id textValidationBlock;

Créez ensuite le AlertViewController comme ceci:

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Title" message:@"Message" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {

}];

   __weak typeof(self) weakSelf = self;
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [weakSelf doSomething];

}];
[alertController addAction:cancelAction];
[alertController addAction:okAction];
[alertController.actions lastObject].enabled = NO;
self.textValidationBlock = [^{
    UITextField *textField = [alertController.textFields firstObject];
    if (something) {
        alertController.message = @"Warning message";
        [alertController.actions lastObject].enabled = NO;
    } else if (somethingElse) {
        alertController.message = @"Another warning message";
        [alertController.actions lastObject].enabled = NO;
    } else {
        //Validation passed
        alertController.message = @"";
        [alertController.actions lastObject].enabled = YES;
    }

} copy];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
    textField.placeholder = @"placeholder here";
    [textField addTarget:weakSelf.textValidationBlock action:@selector(invoke) forControlEvents:UIControlEventEditingChanged];
}];
[self presentViewController:alertController animated:YES completion:nil];
2
Swinny89