具有安全条目的UITextField,在编辑之前始终被清除

Nit*_*hin 40 iphone uitextfield

我有一个奇怪的问题,当我尝试编辑它时,我的UITextField持有一个安全条目总是被清除.我在字段中添加了3个字符,转到另一个字段并返回,光标位于第4个字符位置,但是当我尝试添加另一个字符时,字段中的整个文本被新字符清除.我在笔尖中取消选中"在编辑开始时清除".那么问题是什么?如果我删除了安全入口属性,一切正常,那么,这是安全入口文本字段的属性吗?有什么方法可以防止这种行为吗?

Emp*_*ack 47

组,

textField.clearsOnBeginEditing = NO;
Run Code Online (Sandbox Code Playgroud)

注意:如果secureTextEntry = YES,无效.看来,默认情况下,iOS会在编辑之前清除安全条目文本字段的文本,无论clearsOnBeginEditing是YES还是NO.

  • EmtyStack是对的.感觉有点奇怪,但我认为让它像那样工作是有道理的.当您在安全文本字段中键入时,您只能看到一秒钟的最后一个字符.因此,如果用户返回编辑它,他/她将必须记住他们输入的确切内容,这通常不是这种情况.因此,它清除所有内容以避免用户不断发送错误的安全数据. (2认同)

Eri*_*ric 47

如果您不希望字段清除,即使在secureTextEntry = YES时,请使用:

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

    NSString *updatedString = [textField.text stringByReplacingCharactersInRange:range withString:string];

    textField.text = updatedString;

    return NO;
}
Run Code Online (Sandbox Code Playgroud)

在向注册视图添加显示/隐藏密码文本功能时遇到了类似的问题.

  • 它可以工作,但中间文本的任何更改都会将文本放在文本的末尾. (6认同)

Tho*_*eek 21

如果你正在使用Swift 3,那就给这个子类吧.

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
    }

}
Run Code Online (Sandbox Code Playgroud)

为什么这样做?

TL; DR:如果您在切换时编辑字段isSecureTextEntry,请确保拨打电话becomeFirstResponder.


切换工作的值,isSecureTextEntry直到用户编辑文本字段 - 文本字段在放置新字符之前清除.这个暂定的结算似乎发生在becomeFirstResponder召唤期间UITextField.如果此调用与deleteBackward/ insertTexttrick 结合(如@Aleksey@dwsolberg的答案所示),则保留输入文本,似乎取消了暂定清算.

但是,当isSecureTextEntry文本字段是第一响应者时更改的值(例如,用户键入其密码,来回切换"显示密码"按钮,然后继续键入),文本字段将照常重置.

要在此方案中保留输入文本,becomeFirstResponder只有在textfield是第一个响应者时才会触发此子类.其他答案中似乎缺少此步骤.

谢谢@Patrick Ridd的纠正!

  • 精美小巧,优雅,解决方案.2017/Swift 3中这个问题的最佳答案. (3认同)
  • 哎呀,我发现这有一个由@patrick-ridd 识别的错误。他的解决方案解决了这个问题。请参阅 /sf/answers/3093009181/。 (2认同)
  • 更新您可以设置`enablesReturnKeyAutomatically = false`,但是您会遇到即使字段为空也启用返回键的问题。我不敢相信我在显示/隐藏密码字段上花费了多少时间。 (2认同)
  • 我建议使用 `insertText("\(text)+")`,然后使用 `deleteBackward()`。这可以防止再次成为第一响应者时显示密码字段中的最后一个字符。 (2认同)

Ahm*_*aka 16

@ Eric的答案有效,但我有两个问题.

  1. 正如@malex指出的那样,中间文本的任何更改都会将文本放在文本的末尾.
  2. 我正在使用ReactiveCocoa中的rac_textSignal并直接更改文本不会触发信号.

我的最终代码

- (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;
}
Run Code Online (Sandbox Code Playgroud)

@Mars的Swift3补充:

  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;
}
Run Code Online (Sandbox Code Playgroud)


Sha*_*med 12

斯威夫特 5

yourtextfield.clearsOnInsertion = false
yourtextfield.clearsOnBeginEditing = false
Run Code Online (Sandbox Code Playgroud)

注意:如果secureTextEntry = YES,这将不起作用。似乎默认情况下,iOS 在编辑之前清除安全输入文本字段的文本,无论 clearsOnBeginEditing 是 YES 还是 NO。

简单的方法使用类及其工作 100%

class PasswordTextField: UITextField {

    override var isSecureTextEntry: Bool {
        didSet {
            if isFirstResponder {
                _ = becomeFirstResponder()
                //MARK:- Do something what you want
            }
        }
    }

    override func becomeFirstResponder() -> Bool {

        let success = super.becomeFirstResponder()
        if isSecureTextEntry, let text = self.text {
            self.text?.removeAll()
            insertText(text)
        }
         return success
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 为 self.text = text 更改 insertText(text) 解决了如果点击已填充的文本字段会显示最后一个字符的错误 (3认同)
  • 这个答案是我一年前的答案的直接副本:/sf/answers/3060075931/ (3认同)
  • 杰出的!这实际上是我需要的! (2认同)
  • 要在激活文本字段时隐藏最后一个字符,请添加一个额外的字符,然后退格它,因此在插入之后执行: insertText("x"); 向后删除(); (2认同)

小智 7

@Thomas Verbeek的回答对我帮助很大:

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
    }

}
Run Code Online (Sandbox Code Playgroud)

除了我确实在我的代码中发现了一个错误.在实现他的代码之后,如果textField中有文本并点击textField框,它将只删除第一个字符,然后再次插入所有文本.基本上再次粘贴文本.

为了解决这个问题,我deleteBackward()用a 替换了self.text?.removeAll()它,它就像一个魅力.

没有托马斯的原始解决方案,我就不会那么远,所以谢谢托马斯!

  • 使用此解决方案,当您点击已填充的文本字段时,它将显示最后一个字符。为了解决这个bug,修改insertText(text) for self.text = text (2认同)

Iae*_*all 6

iOS 12、斯威夫特 4

  • 当文本字段再次成为第一响应者时,不显示最后一个字符。
  • 当您重复触摸文本字段时,不会重复现有文本。

如果您不仅想将此解决方案用于安全文本输入,请添加 isSecureTextEntry 检查。

class PasswordTextField: UITextField {
    override func becomeFirstResponder() -> Bool {
        let wasFirstResponder = isFirstResponder
        let success = super.becomeFirstResponder()
        if !wasFirstResponder, let text = self.text {
            insertText("\(text)+")
            deleteBackward()
        }
        return success
    }
}
Run Code Online (Sandbox Code Playgroud)


Amp*_*4nd 5

我遇到了类似的问题.我在表视图中嵌入了登录(secureEntry = NO)和密码(secureEntry = YES)文本字段.我尝试过设置

textField.clearsOnBeginEditing = NO;
Run Code Online (Sandbox Code Playgroud)

在两个相关的委托方法(textFieldDidBeginEditing和textFieldShouldBeginEditing)里面,这些都没有用.从密码字段移动到登录字段后,如果我尝试删除单个字符,整个登录字段将被清除.我使用textFieldShouldChangeCharactersInRange来解决我的问题,如下所示:

-(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;
    }
}
Run Code Online (Sandbox Code Playgroud)

当用户输入退格时,range.length == 1返回true,这是(奇怪)唯一一次我看到该字段被清除.


Ale*_*sey 5

我已经在这里和那里尝试了所有的解决方案,最后带来了压倒一切:

- (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;
}
Run Code Online (Sandbox Code Playgroud)

它会触发UITextField的清除,然后恢复原始文本.


dws*_*erg 5

基于 Aleksey 的解决方案,我正在使用这个子类。

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
    }
}
Run Code Online (Sandbox Code Playgroud)

更改范围内的替换字符对我来说不起作用,因为我有时会用它做其他事情,并且添加更多字符可能会导致错误。

这很好,因为它几乎可以完美地工作。唯一奇怪的是,当您重新点击该字段时,最后一个字符会再次显示。我实际上很喜欢它,因为它充当了一种占位符。