web-dev-qa-db-fra.com

Détecter un événement de retour arrière dans UITextField

Je cherche des solutions sur la façon de capturer un événement de retour arrière, la plupart des réponses Stack Overflow sont en Objective-C mais j'ai besoin de la langue Swift.

J'ai d'abord défini délégué pour UITextField et je l'ai défini sur self

self.textField.delegate = self;

Ensuite, je sais utiliser la méthode déléguée shouldChangeCharactersInRange pour détecter si un retour arrière a été enfoncé si tout le code est en Objective-C. J'ai besoin en Swift ces méthodes suivantes comme ci-dessous sont utilisées.

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    const char * _char = [string cStringUsingEncoding:NSUTF8StringEncoding];
    int isBackSpace = strcmp(_char, "\b");

    if (isBackSpace == -8) {
        // NSLog(@"Backspace was pressed");
    }

    return YES;
}
28
Want Query

Swift 4.2

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    if let char = string.cString(using: String.Encoding.utf8) {
        let isBackSpace = strcmp(char, "\\b")
        if (isBackSpace == -92) {
            print("Backspace was pressed")
        }
    }
    return true
}

Ancienne Swift version

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let  char = string.cStringUsingEncoding(NSUTF8StringEncoding)!
    let isBackSpace = strcmp(char, "\\b")

    if (isBackSpace == -92) {
        println("Backspace was pressed")
    }
    return true
}
63
Long Pham

Dans Swift

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let  char = string.cString(using: String.Encoding.utf8)!
    let isBackSpace = strcmp(char, "\\b")

    if (isBackSpace == -92) {
         print("Backspace was pressed")
    }
    return true
}

:)

18
Insou

Je préfère sous-classer UITextField et remplacer deleteBackward() parce que c'est beaucoup plus fiable que le hack d'utiliser shouldChangeCharactersInRange:

class MyTextField: UITextField {
    override public func deleteBackward() {
        if text == "" {
             // do something when backspace is tapped/entered in an empty text field
        }
        // do something for every backspace
        super.deleteBackward()
    }
}

Le hack shouldChangeCharactersInRange combiné à un caractère invisible placé dans le champ de texte présente plusieurs inconvénients:

  • avec un clavier attaché, on peut placer le curseur devant le caractère invisible et le retour arrière n'est plus détecté,
  • l'utilisateur peut même sélectionner ce caractère invisible (en utilisant Shift Arrow sur un clavier ou même en tapant sur le curseur) et sera confus à propos de ce caractère étrange,
  • la barre de saisie semi-automatique propose des choix étranges tant qu'il n'y a que ce caractère invisible,
  • Les claviers en langue asiatique qui ont des options candidates basées sur le texte du champ de texte seront confus,
  • le placeholder n'est plus affiché,
  • le bouton d'effacement est affiché même s'il ne devrait pas l'être pour clearButtonMode = .whileEditing.

Bien sûr, remplacer deleteBackward() est un peu gênant en raison du besoin de sous-classement. Mais le meilleur UX en vaut la peine!

Et si le sous-classement est interdit, par exemple lors de l'utilisation de UISearchBar avec son UITextField intégré, le swizzling de la méthode devrait également convenir.

16
Ortwin Gentz

Si vous avez besoin de détecter le retour arrière, même dans textField vide (par exemple, si vous avez besoin de basculer automatiquement en arrière vers textField précédent en appuyant sur backSpace), vous pouvez utiliser une combinaison de méthodes proposées - ajouter un signe invisible et utiliser la méthode déléguée standard textField:shouldChangeCharactersInRange:replacementString: comme suivre

  1. Créer un signe invisible

    private struct Constants {
        static let InvisibleSign = "\u{200B}"
    }
    
  2. Définir le délégué pour textField

    textField.delegate = self
    
  3. Lors de l'événement EditingChanged vérifiez le texte et si nécessaire ajoutez un symbole invisible comme suit:

    @IBAction func didChangeEditingInTextField(sender: UITextField) {
        if var text = sender.text {
            if text.characters.count == 1 && text != Constants.InvisibleSign {
                text = Constants.InvisibleSign.stringByAppendingString(text)
                sender.text = text
            }
        }
    }
    

enter image description here

  1. Ajouter l'implémentation de la méthode déléguée textField:shouldChangeCharactersInRange:replacementString:

    extension UIViewController : UITextFieldDelegate {
        // MARK: - UITextFieldDelegate
        func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    
            let  char = string.cStringUsingEncoding(NSUTF8StringEncoding)!
            let isBackSpace = strcmp(char, "\\b")
    
            if (isBackSpace == -92) {
                if var string = textField.text {
                    string = string.stringByReplacingOccurrencesOfString(Constants.InvisibleSign, withString: "")
                    if string.characters.count == 1 {
                        //last visible character, if needed u can skip replacement and detect once even in empty text field
                        //for example u can switch to prev textField 
                        //do stuff here                            
                    }
                }
            }
            return true
        }
    }
    
11
gbk

Essaye ça

public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

if(string == "") {

        print("Backspace pressed");
        return true;
    }
}

Remarque: Vous pouvez retourner "true" si vous souhaitez autoriser le retour arrière. Sinon, vous pouvez retourner "false".

7
Augustine P A

Swift 4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

            let  char = string.cString(using: String.Encoding.utf8)!
            let isBackSpace = strcmp(char, "\\b")

            if isBackSpace == -92 {
                print("Backspace was pressed")
                return false
            }
}
5
Amr Angry

Swift 4

Je trouve la comparaison utilisant strcmp non pertinente. Nous ne savons même pas comment strcmp fonctionne derrière les hottes. Dans toutes les autres réponses en comparant les caractères actuels et \b, Les résultats sont -8 Dans l'objectif-C et -92 Dans Swift. J'ai écrit cette réponse parce que les solutions ci-dessus ne fonctionnaient pas pour moi. (Version Xcode 9.3 (9E145) en utilisant Swift 4.1)

FYI: Chaque caractère que vous tapez réellement est un tableau de 1 Ou plusieurs éléments dans utf8 Encoding. Le caractère backSpace est [0]. Vous pouvez l'essayer.

PS: N'oubliez pas d'assigner le bon delegates à votre textFields.

public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    let  char = string.cString(using: String.Encoding.utf8)!
    if (char.elementsEqual([0])) {
        print("Backspace was pressed")
    }
    else {
        print("WHAT DOES THE FOX SAY ?\n")
        print(char)
    }
    return true
}
4
GreenCamper

S'il vous plaît ne jetez pas votre code. Mettez simplement cette extension quelque part dans votre code. (Swift 4.1)

extension String {
  var isBackspace: Bool {
    let char = self.cString(using: String.Encoding.utf8)!
    return strcmp(char, "\\b") == -92
  }
}

Et puis il suffit de l'utiliser dans vos fonctions

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
  if string.isBackspace {
    // do something
  }
  return true
}
4
Dmitry Kozlov

J'ai implémenté cette fonctionnalité:

enter image description here

Et dans le cas où le dernier textFiled est vide, je veux juste passer au textFiled précédent. J'ai essayé toutes les réponses ci-dessus, mais personne ne fonctionne bien dans ma situation. Pour une raison quelconque, si j'ajoute plus de logique que print dans isBackSpace == -92 les parenthèses bloquent cette méthode qui vient d'arrêter le travail ...

Quant à moi, la méthode ci-dessous est plus élégante et fonctionne comme un charme:

Rapide

class YourTextField: UITextField {

    // MARK: Life cycle

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    // MARK: Methods

    override func deleteBackward() {
        super.deleteBackward()

        print("deleteBackward")
    }

}

Merci @LombaX pour le réponse

3
Roman Romanenko

Swift 4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    //MARK:- If Delete button click
    let  char = string.cString(using: String.Encoding.utf8)!
    let isBackSpace = strcmp(char, "\\b")

    if (isBackSpace == -92) {
        print("Backspace was pressed")

        return true
    }
}
1
Shakeel Ahmed

Swift 4: Si l'utilisateur appuie sur le bouton de retour arrière, la chaîne est vide, de sorte que cette approche force textField à n'accepter que les caractères d'un jeu de caractères spécifié (dans ce cas, les caractères utf8) et les espaces arrière (string.isEmpty case) .

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if string.cString(using: String.Encoding.utf8) != nil {
        return true
    } else if string.isEmpty {
        return true
    } else {
        return false
    }
}
1
Nimz