2011-05-11 6 views
13

Cuando UITextField no contiene nada, presionar la tecla borrar del teclado no llamará a ninguno de los métodos de UITextFieldDelegate.¿Puedo detectar la clave de eliminación incluso si el UITextField está vacío?

¿Cómo puedo detectarlo?

EDITAR: Parece que no hay una manera trivial de hacerlo. Los enlaces más útiles que podemos encontrar son:

  1. UITextField : Any way to detect the Delete key event when the field is empty ?

  2. How to get the actual key pressed in a UITextField

En resumen, mi solución es poner un espacio permanente en el inicio del campo de texto. Y realice otros cambios adicionales (textFieldShouldReturn :, textField: shouldChangeCharactersInRange: replacementString :, etc.).

+0

Ver [respuesta de JacobCaraballo] (http://stackoverflow.com/a/15801258/242933) a a [pregunta similar] (http: // stackoverflow.com/questions/1977934/detect-backspace-in-uitextfield). – ma11hew28

Respuesta

1

Es posible poner un botón sobre él, pero eso es un poco frágil ya que el diseño del teclado podría cambiar con diferentes versiones de iOS y ciertamente hay diferentes diseños de teclado para diferentes idiomas.

Consulte el documento Apple "Managing Text Fields and Text Views". Estoy seguro de que hay una manera de hacer esto. Es algo así como un UITextField implementa un protocolo para obtener keyevents. Uno de esos eventos clave probablemente será una tecla de eliminación, por lo que puede anular el método que reciba y obtenerlo de esa manera.

+0

Leí el documento. No puedo encontrar los "eventos clave" que mencionaste. ¿Podría proporcionarnos más información explícita? – yehnan

+0

Lo siento, lo intenté, pero parece que no funciona como estaba pensando: UITextField * tf = [UITextField new]; BOOL r = [tf respondsToSelector: @selector (deleteBackward :)]; Esperaba que 'r' arriba sea verdadero. Entonces tal vez UITextField implemente estos métodos internamente, dentro de una subclase privada, por lo que no puede anular este método para hacer lo que usted dice. No lo se. Lo siento. – DavidN

+0

Eso está bien. No hay necesidad de disculparse. Dispuesto a probar todas las posibilidades. Gracias. – yehnan

1

Coloque un botón invisible sobre ella.

1

No estoy seguro pero puede intentar un método personalizado usando addTarget: action: forControlEvents:. Pruebe la opción UIControlEventAllEditingEvents.

+0

Buen intento, pero 'UIControlEventAllEditingEvents' no captura la eliminación si el campo de texto está vacío. Tampoco 'UIControlEventAllEvents'. – ma11hew28

4

Ok, lo he descubierto. por lo que en el método delegado - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text (es decir, cuando el texto está a punto de ser introducido) sólo tiene que hacer un

if (text.length <= 0) { 
    // backspace 
    return FALSE; 
} else { 

porque para la tecla de retroceso se text.length == 0, y todo lo demás se == 1.

+3

Sí, tienes razón. Sin embargo, mi pregunta es sobre UITextField. TextoFieldDelegate de UITextFieldDelegate: shouldChangeCharactersInRange: replacementString: no se invocará cuando el campo de texto no contenga nada. Este comportamiento es diferente de UITextView. – yehnan

+0

Entonces Use UITextView. Es simple para reescribir – Oleg

+0

Tiene sentido +1 – StuartM

-1

al utilizar el teclado, es obvio que está escribiendo a alguna variable (por ejemplo: UITextField, UILabel o incluso una cadena global)

una opción para detectar la acción de eliminación puede ser:

  • Mantenga un mundial "int previousLengthOfText"
  • Init esta var ser lastLengthOfText = 0;
  • captura el cambio en la función de delegado: por ejemplo:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

  • comparar la longitud de la etiqueta \ textField a esta var global y ver si un personaje fue agregado o eliminado.

e.g:

if ([self.myTextField.text length] > previousLengthOfText) { 
{ 
// user deleted a character - Do your action here 
previousLengthOfText = [self.myTextField.text length]; 
} 
5

Esto parece posible mediante la subclase UITextField. UITextField se ajusta a un protocolo llamado UITextInput que inturn se ajusta a otro protocolo llamado UIKeyInput. El protocolo UIKeyInput tiene un método deleteBackward que se activa cada vez que se presiona la tecla de retroceso en el teclado.

Aquí es cómo se ve

Cabecera

#import <UIKit/UIKit.h> 

@interface EmptyDeleteTextField : UITextField 

@end 

Implementación

- (void)deleteBackward 
{ 
    NSLog_SM(@"Length: %d", (int)self.text.length); 
    [super deleteBackward]; 
} 

PS: No olvidar llamar súper, ya que no quiere ensuciar con el comportamiento predeterminado de UITextField.

+0

Funciona en mi caso, gracias chico –

2

Esto se puede implementar subclasificando UITextField y agregándolo como clase personalizada del campo de texto deseado. Luego, anule el método "deleteBackward" Este método capturará todos los eventos de retroceso.

Código

en Swift:

override func deleteBackward() { 
     super.deleteBackward() 
     // Enter your stuff here 
    } 

También asegúrese de que está llamando método super demasiado, como lo está haciendo las funcionalidades básicas del evento.

1

Estoy agregando esta respuesta para un ejemplo más completo en swift 3. Básicamente, necesitaba una vista de tipo de código PIN donde tengo múltiples campos de texto que permiten un carácter en cada celda.

como este enter image description here I comenzó mediante la creación de una subclase de UITextField y un protocolo que define un nuevo func.

protocol MYDeleteActionTextFieldDelegate { 
    func textFieldDidSelectDeleteButton(_ textField: UITextField) -> Void 
} 
class MYDeleteActionTextField: UITextField { 
    var deleteDelegate: MYDeleteActionTextFieldDelegate? 
    override func deleteBackward() { 
     // Need to call this before super or the textfield edit may already be in place 
     self.deleteDelegate?.textFieldDidSelectDeleteButton(self) 
     super.deleteBackward() 
    } 
} 

Luego crea los campos de texto con la nueva subclase e implementa el delegado en su controlador de vista. En mi caso, administro los campos de texto en una matriz para facilitar su uso y diseño las celdas con PureLayout. Les tienda como esta

pinFields var = UITextField

Luego, en viewDidLoad(), agrego todos los campos de pasador en la matriz de esta manera:

for _ in 1...6 { 
      let field = EDFDeleteActionTextField.init(forAutoLayout:()) 
      field.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: UIControlEvents.editingChanged) 
      field.delegate = self 
      field.deleteDelegate = self 
      field.textAlignment = .center 
      field.font = UIFont.newBigTitle() 
      field.textColor = UIColor.edfBlue() 
      field.backgroundColor = UIColor.edfWhite() 
      self.pinFields.append(field) 
      self.pinView.addSubview(field) 
     } 

Ahora sólo tiene que responder a toda la apropiada delegar métodos y el objetivo textFieldDidChange que se agregó arriba.

// MARK: UITextFieldDelegate 
    func textFieldDidChange(textField: UITextField) { 
     // If the user typed one character, move to the next cell. 
     if (textField.text?.characters.count == 1) { 
      let index = pinFields.index(of: textField) 
      textField.resignFirstResponder() 
      if (pinFields.count > index! + 1) { 
       pinFields[index! + 1].becomeFirstResponder() 
      } 
     } // If they deleted the character move to previous cell 
     else if (textField.text?.characters.count == 0) { 
      let index = pinFields.index(of: textField) 
      if (index! - 1 >= 0) { 
       pinFields[index! - 1].becomeFirstResponder() 
      } 
     } 
    } 

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 
     if range.location > 0 { 
      let index = pinFields.index(of: textField) 
      // If there is already text in the text field and the next cell is empty - move the newly typed character to that cell. 
      if (pinFields.count > index! + 1) { 
       let nextField = pinFields[index! + 1] 
       if (nextField.text?.characters.count == 0) { 
        textField.resignFirstResponder() 
        nextField.becomeFirstResponder() 
        nextField.text = string 
       } 
      } 
      return false 
     } 
     return true 
    } 

    func textFieldShouldReturn(_ textField: UITextField) -> Bool { 
     textField.resignFirstResponder() 
     return false 
    } 

    // MARK: EDFDeleteActionTextFieldDelegate 
    func textFieldDidSelectDeleteButton(_ textField: UITextField) { 
     // If user clicked delete, and there are no characters, move to previous cell if available. 
     // If there are characters, it is handled in UITextFieldDelegate 
     if (textField.text?.characters.count == 0) { 
      let index = pinFields.index(of: textField) 
      if (index! - 1 >= 0) { 
       pinFields[index! - 1].becomeFirstResponder() 
      } 
      else { 
       textField.resignFirstResponder() 
      } 
     } 
    } 

Voy a dejar fuera las partes aburridas (como trazar los campos de texto, etc), ya que esta funcionalidad general es útil en más casos que este punto de vista de código PIN, pero la implementación de esta clase de niños y protocolo deberá proporcionar toda la funcionalidad que necesitaría para vistas de tipo similar y resolver la pregunta en cuestión (que probablemente necesite algo similar).

Happy coding.

Cuestiones relacionadas