UITextField имеет завершающий пробел после переключения secureTextEntry

У меня есть кнопка, которая переключается между режимами Show/Hide (т.е. переключает UITextField между secureTextEntry NO и YES). Цель которого состоит в том, чтобы позволить пользователю видеть пароль, который он вводит.

Я последовал примеру (с наибольшим количеством голосов) здесь: UITextField secureTextEntry - работает при переходе от YES к NO, но возврат к YES не имеет никакого эффекта

Однако, когда я устанавливаю для параметра secureTextEntry значение NO, любой написанный там текст заканчивается пробелом в конце. Это не проблема, если для параметра secureTextEntry установлено значение YES.

Например, если я ввожу текст «mypassword», когда setSecureTextEntry имеет значение NO, а затем переключаю его на YES, пользователь увидит ********** (10 точек), что правильно. Если я установлю для SecureTextEntry значение NO, пользователь увидит «mypassword» (с пробелом в конце или, по крайней мере, курсор переместится на один пробел вправо).

Важное примечание. В отладчике строковое значение text отображается без завершающего пробела, например:

(lldb) expr self.passwordText.text
(NSString *) $0 = 0x1d8450e0 @"mypassword"

Я попытался обрезать пробелы (в соответствии с избегайте пробелов посередине в UITextField), но у него было нет эффекта.


person Brian Colavito    schedule 08.01.2013    source источник
comment
Я думаю, что iOS продолжает рисовать мигающий курсор в той же позиции. Точки шире текста, поэтому между текстом и курсором всегда есть зазор. «Пробел» на самом деле не в текстовом поле, он просто выглядит так из-за размещения курсора.   -  person russau    schedule 23.01.2013


Ответы (18)


Я только что столкнулся с этим случаем и, наконец, решил эту проблему.

работает на последнем iOS SDK, iOS 8.1

Во-первых, вообще нет пробела.

Символ точки (показан в SecureEntry) и обычный символ имеют разную ширину, и после переключения переключателя isSecureEntry курсор не обновлял свою позицию.

поэтому я использую этот обходной путь для решения этой проблемы.

- (void)toggle
{
    NSString *tmpString;
    [self.passwordTextField setSecureTextEntry:!self.passwordTextField.isSecureTextEntry];
    if (self.passwordTextField.isSecureTextEntry) {
       // do stuffs
    } else {
       // do other stuffs
    }
    // Workaround to refresh cursor
    tmpString = self.passwordTextField.text;
    self.passwordTextField.text = @" ";
    self.passwordTextField.text = tmpString;
}

Свифт 3+

   // Workaround to refresh cursor

    let currentText: String = self.userPassword.text!
    self.userPassword.text = "";
    self.userPassword.text = currentText

Надеюсь, поможет!

person sunus    schedule 15.12.2014
comment
Эти 3 строки для обновления курсора избавляются от пробела // Workaround to refresh cursor tmpString = self.passwordTextField.text; self.passwordTextField.text = @" "; self.passwordTextField.text = tmpString; - person simple_code; 07.01.2016

PRE-iOS-8.0 (устаревшее решение)... В методе действия вашей кнопки (переключение между secureTextEntry ДА и НЕТ) просто установите для свойства text UITextField его текущее значение text. Хотя это может показаться избыточным и немного похожим на хак, это перерисует курсор в правильном положении. Вот пример того, как теперь должен выглядеть метод действия вашей кнопки...

-(void)toggleButtonPressed:(UIButton*)sender
{
    // Toggle between secure and not-so-secure entry
    self.toggleButton.selected = !self.toggleButton.selected;
    self.textfield.secureTextEntry = !self.toggleButton.selected;

    // * Redundant (but necessary) line *
    [self.textfield setText:self.textfield.text];
}

POST-iOS-8.0... Что касается iOS 8.0, похоже, что сеттер UITextField text больше не перерисовывает курсор при вызове со строкой, равной его текущему строковому значению. Теперь нам нужно сделать еще один шаг и фактически изменить значение text, прежде чем сбрасывать его снова. Замените приведенную выше строку setText: чем-то вроде этих строк.

// * Extra redundant (but necessary) lines *
NSString *currentText = self.textfield.text;
[self.textfield setText:@"Arbitrary string..."]; // Change the value
[self.textfield setText:currentText]; // Reset the value
person Matthew    schedule 20.03.2014
comment
Вы правы - похоже, что сеттер UITextField iOS 8.0+ text теперь игнорирует повторяющиеся строки... Спасибо @sethfri и другим комментаторам. - person Matthew; 05.09.2015
comment
Это отлично работало в 9.3 textField.text = textField.text (после переключения безопасного ввода текста) - person 0x6A75616E; 28.03.2016

У меня есть чистое решение, которое не пачкается со свойством text UITextField.

Оберните их в этом стиле.

[self.passwordTextField resignFirstResponder]; // first resign its first responder.

// change `secureTextEntry` property's value if necessary.

if (self.passwordTextField.secureTextEntry) {
    self.passwordTextField.secureTextEntry = NO;
    self.passwordEcryptButton.selected = YES;
}else{
    self.passwordTextField.secureTextEntry = YES;
    self.passwordEcryptButton.selected = NO;
}

[self.passwordTextField becomeFirstResponder];  // finally gain its first responder again.
person tounaobun    schedule 30.11.2015
comment
Отлично. Работает и на iOS 11. Спасибо! - person David Weiss; 15.12.2017

Чтобы обойти эту ошибку в iOS, вы можете просто сделать следующее (работает для любой версии iOS):

- (IBAction)toggleSecureTextEntry:(UIButton *)button
{
    self.textField.secureTextEntry = !self.textField.secureTextEntry;
    NSString *originalText = self.textField.text;
    self.textField.text = nil;
    self.textField.text = originalText;
}
person nalexn    schedule 16.06.2015

Вы можете исправить это следующим образом:

NSString *currentText = self.textfield.text;
self.textfield.text = @"";
self.textfield.text = currentText;
person Kingiol    schedule 29.09.2015

Это работает для меня на iOS 8

if (self.passwordTextField.secureTextEntry) {
    // Display password and keep selected text range
    UITextRange *selectedTextRange = self.passwordTextField.selectedTextRange;
    NSString *password = self.passwordTextField.text;
    self.passwordTextField.secureTextEntry = NO;
    self.passwordTextField.text = [@"" stringByPaddingToLength:password.length withString:@" " startingAtIndex:0]; // Done for carret redrawing
    self.passwordTextField.text = password;
    self.passwordTextField.selectedTextRange = selectedTextRange;
}
else {
    // Hide password and keep selected text range
    UITextRange *selectedTextRange = self.passwordTextField.selectedTextRange;
    NSString *password = self.passwordTextField.text;
    self.passwordTextField.secureTextEntry = YES;
    self.passwordTextField.text = [@"" stringByPaddingToLength:password.length withString:@" " startingAtIndex:0]; // Done for carret redrawing
    self.passwordTextField.text = password;
    self.passwordTextField.selectedTextRange = selectedTextRange;
}
person Gonzo Oin    schedule 09.10.2014

UITextPosition *beginning = [self.passwordField beginningOfDocument];
[self.passwordField setSelectedTextRange:[self.passwordField textRangeFromPosition:beginning
                                                                        toPosition:beginning]];

UITextPosition *end = [self.passwordField endOfDocument];
[self.passwordField setSelectedTextRange:[self.passwordField textRangeFromPosition:end
                                                                        toPosition:end]];

Это то, что я использовал для iOS 8

person David Carvalho    schedule 03.12.2014

Когда мы изменяем свойство textfield.secureTextEntry, позиция каретки не обновляется. Чтобы исправить это, приведенный ниже код работал до IOS 8:

pwdTextField.text  = pwdTextField.text

Теперь это не так. Кажется, IOS 8 обнаруживает, что новое значение равно старому значению, и ничего не делает. Поэтому, чтобы заставить его работать снова, мы должны фактически изменить значение. Вот быстрая версия, которая работает для меня.

let str = pwdTextField.text
pwdTextField.text = str + " "
pwdTextField.text = str
person KeepWalking    schedule 03.12.2014

Это еще одна возможность решить эту проблему, где self.passwordText является UITextField:

if (self.passwordText.isFirstResponder) {
    [self.passwordText resignFirstResponder];
    [self.passwordText becomeFirstResponder];
}
person mc_plectrum    schedule 20.02.2015

Похоже, что второе решение в указанной ссылке при реализации имеет желаемое поведение без добавления дополнительного пробела:

https://stackoverflow.com/a/8495888/738190

person Brian Colavito    schedule 08.01.2013

Это работает в моем случае

    BOOL wasFirstResponder = [self.passwordTextField isFirstResponder];
    if([self.passwordTextField isSecureTextEntry])
    {
        //This three lines are key
        self.passwordTextField.delegate = nil;
        [self.passwordTextField resignFirstResponder];
        self.passwordTextField.delegate = self;
    }

    [self.passwordTextField setSecureTextEntry: !self.passwordTextField.isSecureTextEntry];
    if(wasFirstResponder)
        [self.passwordTextField becomeFirstResponder];
person Mihir Mehta    schedule 18.06.2015

Расширение Swift UITextField:

extension UITextField {
    func toggleSecureEntry() {
        let wasFirstResponder = isFirstResponder

        if wasFirstResponder { resignFirstResponder() }
        isSecureTextEntry.toggle()
        if wasFirstResponder { becomeFirstResponder() }
    }
}

Установка решения textField.text также работает в некоторых ситуациях, но не для моих нужд (Пользовательский шрифт с двумя текстовыми полями. Вызывает изменения шрифта и сбои во время выполнения.) Добавление сюда тоже.

func toggleSecureEntry() {
    isSecureTextEntry.toggle()
    let originalText = text
    text = nil
    text = originalText
}
person anilgoktas    schedule 22.05.2016

Чтобы правильно изменить положение курсора, установка атрибутов шрифта, похоже, помогла мне.

// Hack to update cursor position
self.passwordTf.defaultTextAttributes = @{NSFontAttributeName: textFieldFont, NSForegroundColorAttributeName: textFieldColor};

// Change secure entry
self.passwordTf.secureTextEntry = !self.passwordTf.secureTextEntry;

Проверено на iOS8, iOS9. Надеюсь, поможет!

person Rahul    schedule 26.09.2015

Каждый раз, когда текст устанавливается в UITextField, положение курсора обновляется.

Поэтому я использовал этот код

partial void btnShowPassword_ToutchUpInside (UIButton sender)
    {
        if (TxtPassword.SecureTextEntry == true) {
            TxtPassword.SecureTextEntry = false;
            TxtPassword.Text = TxtPassword.Text;
        } else {
            TxtPassword.SecureTextEntry = true;
        }
    }
person Hans-Peter    schedule 13.04.2016

Вот решение:

- (void)showHidePassword:(UIButton *)sender {
    EDSignUpCell *cell = [self.signUpTblView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:3 inSection:0]];
    if(!TRIM_SPACE(cell.cellTextField.text).length) {return;}
    [cell.showHidePasswordBtn setSelected:!cell.showHidePasswordBtn.isSelected];
    cell.cellTextField.secureTextEntry = cell.showHidePasswordBtn.isSelected;
    [cell.cellTextField setText:cell.cellTextField.text];
    [cell.cellTextField becomeFirstResponder];
}
person DHARMENDRA SINHA    schedule 25.06.2018
comment
Пожалуйста, попробуйте отформатировать код вашего ответа, чтобы он выглядел более читабельным. - person Failed Scientist; 25.06.2018
comment
@tuomastik, я одобрил ваше редактирование, но вы случайно нарушили код, и мне пришлось его исправить. Пожалуйста, не удаляйте методы экземпляра - for instance в target-c. - person Cœur; 25.06.2018

Я использую это, работает нормально.

[self.yourTextField becomeFirstResponder];
person Ramdhas    schedule 31.08.2016

Свифт 4

Ошибка находится на радаре, также есть объяснение обходного пути: http://www.openradar.me/38465011

Вот вырезка из временного обходного пути, как обновить позицию каретки (курсора).

// update caret position
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
    let (beginning, end) = (self.beginningOfDocument, self.endOfDocument)

    self.selectedTextRange = self.textRange(from: beginning, to: end)
    self.selectedTextRange = self.textRange(from: end, to: end)
}
person dimpiax    schedule 17.08.2018

У меня была аналогичная проблема, и я понял, что это было из-за того, что я обновлял текст до установки свойства secureTextEntry. Имеет смысл, что textField будет рисовать курсор в том месте, где он был бы, если бы он использовал secureTextEntry.

Я не читал всю проблему и не посещал решение, на которое ссылается OP, но на случай, если у кого-то возникнет такая же проблема, как у меня:

Попробуйте обновить текст после установки свойства secureTextEntry.

person redmage1993    schedule 23.04.2019