varios - eliminar lista de recordatorios iphone
UITextField con entrada segura, que siempre se borra antes de editar (21)
Tengo un problema extraño en el que mi UITextField, que contiene una entrada segura, siempre se borra cuando intento editarlo. Agregué 3 caracteres al campo, fui a otro campo y regresé, el cursor está en la posición 4ª, pero cuando intento agregar otro carácter, todo el texto del campo se borra con el nuevo carácter. Tengo ''Se borra cuando comienza la edición'' sin marcar en la plumilla. Entonces, ¿cuál sería el problema? Si elimino la propiedad de entrada segura, todo funciona bien, entonces, ¿es esta propiedad de los campos de texto de entrada segura? ¿Hay alguna forma de prevenir este comportamiento?
Basado en la solución de Aleksey, estoy usando esta subclase.
class SecureNonDeleteTextField: UITextField {
override func becomeFirstResponder() -> Bool {
guard super.becomeFirstResponder() else { return false }
guard self.secureTextEntry == true else { return true }
guard let existingText = self.text else { return true }
self.deleteBackward() // triggers a delete of all text, does NOT call delegates
self.insertText(existingText) // does NOT call delegates
return true
}
}
Cambiar los caracteres de reemplazo en el rango no funciona para mí porque a veces hago otras cosas con eso, y agregar más hace que sea más probable que tenga errores.
Esto es bueno porque funciona bastante bien. La única rareza es que el último carácter se muestra de nuevo cuando vuelves al campo. En realidad me gusta eso porque actúa como una especie de marcador de posición.
Conjunto,
textField.clearsOnBeginEditing = NO;
Nota: Esto no funcionará si secureTextEntry = YES . Parece que, de forma predeterminada, iOS borra el texto de los campos de texto de entrada seguros antes de editar, no importa clears OnNeginEditing is YES o NO.
Después de jugar con la solución de @malex llegué a esta versión de Swift :
1) No olvide hacer de su controlador de vista un UITextFieldDelegate:
class LoginViewController: UIViewController, UITextFieldDelegate {
...
}
override func viewDidLoad() {
super.viewDidLoad()
textfieldPassword.delegate = self
}
2) usa esto:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if let start: UITextPosition = textField.positionFromPosition(textField.beginningOfDocument, offset: range.location),
let end: UITextPosition = textField.positionFromPosition(start, offset: range.length),
let textRange: UITextRange = textField.textRangeFromPosition(start, toPosition: end) {
textField.replaceRange(textRange, withText: string)
}
return false
}
... y si está haciendo esto para la función "mostrar / ocultar contraseña" lo más probable es que necesite guardar y restaurar la posición de intercalación cuando active / desactive la entrada segura de texto. Aquí es cómo (hacerlo dentro del método de conmutación):
var startPosition: UITextPosition?
var endPosition: UITextPosition?
// Remember the place where cursor was placed before switching secureTextEntry
if let selectedRange = textfieldPassword.selectedTextRange {
startPosition = selectedRange.start
endPosition = selectedRange.end
}
...
// After secureTextEntry has been changed
if let start = startPosition {
// Restoring cursor position
textfieldPassword.selectedTextRange = textfieldPassword.textRangeFromPosition(start, toPosition: start)
if let end = endPosition {
// Restoring selection (if there was any)
textfieldPassword.selectedTextRange = textfield_password.textRangeFromPosition(start, toPosition: end)
}
}
Este es un código Swift de mi proyecto, probado y con cambios de retroceso y entrada segura false / true
// obtener la entrada del usuario y llamar a los métodos de validación
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (textField == passwordTextFields) {
let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
// prevent backspace clearing the password
if (range.location > 0 && range.length == 1 && string.characters.count == 0) {
// iOS is trying to delete the entire string
textField.text = newString
choosPaswwordPresenter.validatePasword(text: newString as String)
return false
}
// prevent typing clearing the pass
if range.location == textField.text?.characters.count {
textField.text = newString
choosPaswwordPresenter.validatePasword(text: newString as String)
return false
}
choosPaswwordPresenter.validatePasword(text: newString as String)
}
return true
}
Experimenté con respuestas de dwsolberg y fluidsonic y esto parece funcionar.
override func becomeFirstResponder() -> Bool {
guard !isFirstResponder else { return true }
guard super.becomeFirstResponder() else { return false }
guard self.isSecureTextEntry == true else { return true }
guard let existingText = self.text else { return true }
self.deleteBackward() // triggers a delete of all text, does NOT call delegates
self.insertText(existingText) // does NOT call delegates
return true
}
Guardar el texto introducido en una propiedad. Por ejemplo:
NSString *_password;
Y luego en el delegado:
textFieldDidBeginEditing:(UITextField *)textField
assign textField.text = _password;
Hay otra pregunta de post: Question
Al leer esa publicación parece que necesitas configurar el textField.clearsOnBeginEditing = NO en el método de delegado textFieldShouldBeginEditing
He intentado todas las soluciones aquí y allá y finalmente vine con esa anulación:
- (BOOL)becomeFirstResponder
{
BOOL became = [super becomeFirstResponder];
if (became) {
NSString *originalText = [self text];
//Triggers UITextField to clear text as first input
[self deleteBackward];
//Requires setting text via ''insertText'' to fire all associated events
[self setText:@""];
[self insertText:originalText];
}
return became;
}
Activa el claro de UITextField y luego restaura el texto original.
La respuesta de @ Eric funciona, pero tuve dos problemas con eso.
- Como @malex señaló, cualquier cambio de texto en el centro colocará el carro al final del texto.
- Estoy usando rac_textSignal de ReactiveCocoa y cambiar el texto directamente no dispararía la señal.
Mi código final
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
//Setting the new text.
NSString *updatedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
textField.text = updatedString;
//Setting the cursor at the right place
NSRange selectedRange = NSMakeRange(range.location + string.length, 0);
UITextPosition* from = [textField positionFromPosition:textField.beginningOfDocument offset:selectedRange.location];
UITextPosition* to = [textField positionFromPosition:from offset:selectedRange.length];
textField.selectedTextRange = [textField textRangeFromPosition:from toPosition:to];
//Sending an action
[textField sendActionsForControlEvents:UIControlEventEditingChanged];
return NO;
}
Swift3 además de @Mars:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let nsString:NSString? = textField.text as NSString?
let updatedString = nsString?.replacingCharacters(in:range, with:string);
textField.text = updatedString;
//Setting the cursor at the right place
let selectedRange = NSMakeRange(range.location + string.length, 0)
let from = textField.position(from: textField.beginningOfDocument, offset:selectedRange.location)
let to = textField.position(from: from!, offset:selectedRange.length)
textField.selectedTextRange = textField.textRange(from: from!, to: to!)
//Sending an action
textField.sendActions(for: UIControlEvents.editingChanged)
return false;
}
La respuesta de @Thomas Verbeek me ayudó mucho:
class PasswordTextField: UITextField {
override var isSecureTextEntry: Bool {
didSet {
if isFirstResponder {
_ = becomeFirstResponder()
}
}
}
override func becomeFirstResponder() -> Bool {
let success = super.becomeFirstResponder()
if isSecureTextEntry, let text = self.text {
deleteBackward()
insertText(text)
}
return success
}
}
Excepto que encontré un error en mi código con él. Después de implementar su código, si tiene texto en el campo de texto y toca el cuadro de campo de texto, solo eliminará el primer carácter y luego insertará todo el texto nuevamente. Básicamente pegando el texto de nuevo.
Para remediar esto, reemplacé el deleteBackward()
con un self.text?.removeAll()
y funcionó como un encanto.
No hubiera llegado tan lejos sin la solución original de Thomas, ¡así que gracias Thomas!
Lo resolvimos en base a la respuesta de dwsolberg con dos soluciones:
- el texto de la contraseña se duplicó al tocar un campo de contraseña ya enfocado
- el último carácter de la contraseña se reveló al tocar el campo de contraseña
-
deleteBackward
einsertText
provocan que seinsertText
el evento de cambio de valor
Entonces se nos ocurrió esto (Swift 2.3):
class PasswordTextField: UITextField {
override func becomeFirstResponder() -> Bool {
guard !isFirstResponder() else {
return true
}
guard super.becomeFirstResponder() else {
return false
}
guard secureTextEntry, let text = self.text where !text.isEmpty else {
return true
}
self.text = ""
self.text = text
// make sure that last character is not revealed
secureTextEntry = false
secureTextEntry = true
return true
}
}
Me doy cuenta de que es un poco viejo, pero en iOS 6, el "texto" de UITextField ahora está por defecto "Atribuido" en Interface Builder. Cambiar esto a "simple", que es como estaba en iOS 5, soluciona este problema.
También publicó esta misma respuesta en la pregunta que @Craig vinculó.
Mi solución (hasta que se solucione el error, supongo) es la subclase UITextField de modo que se agregue al texto existente en lugar de borrarse como antes (o como en iOS 6). Intenta preservar el comportamiento original si no se ejecuta en iOS 7:
@interface ZTTextField : UITextField {
BOOL _keyboardJustChanged;
}
@property (nonatomic) BOOL keyboardJustChanged;
@end
@implementation ZTTextField
@synthesize keyboardJustChanged = _keyboardJustChanged;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
_keyboardJustChanged = NO;
}
return self;
}
- (void)insertText:(NSString *)text {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
if (self.keyboardJustChanged == YES) {
BOOL isIOS7 = NO;
if ([[UIApplication sharedApplication] respondsToSelector:@selector(backgroundRefreshStatus)]) {
isIOS7 = YES;
}
NSString *currentText = [self text];
// only mess with editing in iOS 7 when the field is masked, wherein our problem lies
if (isIOS7 == YES && self.secureTextEntry == YES && currentText != nil && [currentText length] > 0) {
NSString *newText = [currentText stringByAppendingString: text];
[super insertText: newText];
} else {
[super insertText:text];
}
// now that we''ve handled it, set back to NO
self.keyboardJustChanged = NO;
} else {
[super insertText:text];
}
#else
[super insertText:text];
#endif
}
- (void)setKeyboardType:(UIKeyboardType)keyboardType {
[super setKeyboardType:keyboardType];
[self setKeyboardJustChanged:YES];
}
@end
Necesitaba ajustar la solución @ thomas-verbeek, agregando una propiedad que se ocupa del caso cuando el usuario intenta pegar cualquier texto en el campo (el texto ha sido duplicado)
class PasswordTextField: UITextField {
private var barier = true
override var isSecureTextEntry: Bool {
didSet {
if isFirstResponder {
_ = becomeFirstResponder()
}
}
}
override func becomeFirstResponder() -> Bool {
let success = super.becomeFirstResponder()
if isSecureTextEntry, let text = self.text, barier {
deleteBackward()
insertText(text)
}
barier = !isSecureTextEntry
return success
}
}
Si desea utilizar secureTextEntry = YES y el comportamiento visual adecuado para el transporte, necesita esto:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
{
if (!string.length) {
UITextPosition *start = [self positionFromPosition:self.beginningOfDocument offset:range.location];
UITextPosition *end = [self positionFromPosition:start offset:range.length];
UITextRange *textRange = [self textRangeFromPosition:start toPosition:end];
[self replaceRange:textRange withText:string];
}
else {
[self replaceRange:self.selectedTextRange withText:string];
}
return NO;
}
Si estás usando Swift 3 , dale una oportunidad a esta subclase.
class PasswordTextField: UITextField {
override var isSecureTextEntry: Bool {
didSet {
if isFirstResponder {
_ = becomeFirstResponder()
}
}
}
override func becomeFirstResponder() -> Bool {
let success = super.becomeFirstResponder()
if isSecureTextEntry, let text = self.text {
self.text?.removeAll()
insertText(text)
}
return success
}
}
¿Por qué funciona esto?
TL; DR: si está editando el campo mientras isSecureTextEntry
, asegúrese de llamar becomeFirstResponder
.
La conmutación del valor de isSecureTextEntry
funciona bien hasta que el usuario edita el campo de texto; el campo de texto se borra antes de colocar los nuevos caracteres. Esta compensación tentativa parece ocurrir durante la llamada de ser el primer UITextField
de UITextField
. Si esta llamada se combina con el truco deleteBackward
/ insertText
(como se demostró en las respuestas de @Aleksey y @dwsolberg ), el texto de entrada se conserva, aparentemente cancelando la eliminación tentativa.
Sin embargo, cuando el valor de isSecureTextEntry
cambia mientras el campo de texto es el primer respondedor (por ejemplo, el usuario escribe su contraseña, alterna un botón ''mostrar contraseña'' y luego continúa escribiendo), el campo de texto se restablecerá como de costumbre.
Para preservar el texto de entrada en este escenario, esta subclase se becomeFirstResponder
solo si el campo de texto fue el primer respondedor. Este paso parece faltar en otras respuestas.
Gracias a @Patrick Ridd por la corrección!
Si no desea que el campo se borre, incluso cuando secureTextEntry = YES, use:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *updatedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
textField.text = updatedString;
return NO;
}
Encontré un problema similar al agregar la funcionalidad de mostrar / ocultar texto de contraseña en una vista de registro.
Swift 3 / Swift 4
yourtextfield.clearsOnInsertion = false
yourtextfield.clearsOnBeginEditing = false
Nota: Esto no funcionará si secureTextEntry = YES. Parece que, de forma predeterminada, iOS borra el texto de los campos de texto de entrada seguros antes de editar, no importa clears OnNeginEditing is YES o NO.
Fácil uso de la clase y su trabajo 100%.
class PasswordTextField: UITextField {
override var isSecureTextEntry: Bool {
didSet {
if isFirstResponder {
_ = becomeFirstResponder()
}
}
}
override func becomeFirstResponder() -> Bool {
let success = super.becomeFirstResponder()
if isSecureTextEntry, let text = self.text {
self.text?.removeAll()
insertText(text)
}
return success
}
}
Tuve el mismo problema, pero tengo la solución;
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
if(textField==self.m_passwordField)
{
text=self.m_passwordField.text;
}
[textField resignFirstResponder];
if(textField==self.m_passwordField)
{
self.m_passwordField.text=text;
}
return YES;
}
Tuve un problema similar. Tengo campos de texto de inicio de sesión (secureEntry = NO) y contraseña (secureEntry = YES) incrustados en una vista de tabla. Intenté establecer
textField.clearsOnBeginEditing = NO;
dentro de los dos métodos de delegado relevantes (textFieldDidBeginEditing y textFieldShouldBeginEditing), que no funcionó. Después de pasar del campo de contraseña al campo de inicio de sesión, todo el campo de inicio de sesión se borraría si intentara eliminar un solo carácter. Usé el textFieldShouldChangeCharactersInRange para resolver mi problema, de la siguiente manera:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (range.length == 1 && textField.text.length > 0)
{
//reset text manually
NSString *firstPart = [textField.text substringToIndex:range.location]; //current text minus one character
NSString *secondPart = [textField.text substringFromIndex:range.location + 1]; //everything after cursor
textField.text = [NSString stringWithFormat:@"%@%@", firstPart, secondPart];
//reset cursor position, in case character was not deleted from end of
UITextRange *endRange = [textField selectedTextRange];
UITextPosition *correctPosition = [textField positionFromPosition:endRange.start offset:range.location - textField.text.length];
textField.selectedTextRange = [textField textRangeFromPosition:correctPosition toPosition:correctPosition];
return NO;
}
else
{
return YES;
}
}
El range.length == 1 devuelve verdadero cuando el usuario ingresa un retroceso, lo cual es (extrañamente) la única vez que vería el campo borrado.
Usé la respuesta de textField.clearsOnBeginEditing = NO;
en mi campo de texto de passwordTextField.secureTextEntry = YES;
pero no funcionó en iOS11 SDK con Xcode 9.3, así que terminé de seguir el código para lograrlo. En realidad, quiero mantener el texto (en el campo de texto) si el usuario cambia entre diferentes campos.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.tag == 2) {
if ([string isEqualToString:@""] && textField.text.length >= 1) {
textField.text = [textField.text substringToIndex:[textField.text length] - 1];
} else{
textField.text = [NSString stringWithFormat:@"%@%@",textField.text,string];
}
return false;
} else {
return true;
}
}
shouldChangeCharactersInRange
falso en shouldChangeCharactersInRange
y shouldChangeCharactersInRange
ya que quiero que este código también funcione si el usuario hace clic en el botón Eliminar para eliminar el carácter.