开发者

UITextField secureTextEntry - works going from YES to NO, but changing back to YES has no effect

开发者 https://www.devze.com 2023-03-20 19:56 出处:网络
The above says it all- I have a UITextField set to secure, but want to give users the option to make it not secure (so they can see for sure what they typed if they are in a private area). However, su

The above says it all- I have a UITextField set to secure, but want to give users the option to make it not secure (so they can see for sure what they typed if they are in a private area). However, sup开发者_如何学编程pose they hit the toggle by mistake, and want to change it back to secure mode? That does not work. I've tried everything - using -1 instead of YES, deleting the text and then putting it back. I'm at a total loss on other ideas. [Entered rdar://9781908]

EDIT: I believe this issue is fixed as of iOS5.


It must be input-focus issue: when focused, UITextField can change only ON->OFF.
Try next trick to switch OFF->ON:

textField.enabled = NO;
textField.secureTextEntry = YES;
textField.enabled = YES;
[textField becomeFirstResponder];

EDIT (dhoerl): In iOS 9.3, I found this works but there is a problem. If you enter say 3 characters in plain view, then switch to secure text, then type a character, the 3 pre-existing characters disappear. I tried all kinds of trick to clear, then reset the text without success. I finally tried just playing with the control by cutting and pasting - pasting into the newly-switched-to-secure-mode worked great. So I emulated it in code, and now it all works fine - no need to play with the responder either. Here is what I finally ended up with:

    if textview should go into secure mode and the textfield is not empty {
        textField.text = ""
        textField.secureTextEntry = true

        UIPasteboard.generalPasteboard().string = password
        textField.paste(self)
        UIPasteboard.generalPasteboard().string = ""
    }

I'd prefer to not have to do this paste, but if you want it seamless this is the only way I can find to do it.


Mike R's solution is nice, but I prefer this approach:

BOOL wasFirstResponder;
if ((wasFirstResponder = [passwordField isFirstResponder])) {
    [passwordField resignFirstResponder];
}
// In this example, there is a "show password" toggle
[passwordField setSecureTextEntry:![passwordField isSecureTextEntry]];
if (wasFirstResponder) {
    [passwordField becomeFirstResponder];
}

That way you only becomeFirstResponder again when necessary.


I entered rdar against this problem, but did find a work around. Essentially you have to programmatically replace the "stuck" control with a new one. The easiest thing to do is to archive the existing control in viewDidLoad then unarchive as needed:

// do in viewDidLoad
self.passwordMemberArchive = [NSMutableData data];
NSKeyedArchiver *ka = [[NSKeyedArchiver alloc]       initForWritingWithMutableData:passwordMemberArchive];
[ka encodeObject:password];
[ka finishEncoding];
[ka release];


    // In the action method when you get the UISwitch action message ---
// when your switch changes state
if(isOn) {
    NSString *text = [NSString stringWithString:password.text];
    NSKeyedUnarchiver *kua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    UITextField *tf = [kua decodeObject];
    [kua finishDecoding];
    [kua release];
    tf.inputAccessoryView = textField.inputAccessoryView;
    tf.frame = textField.frame;

    BOOL isFirstResponder = [textField isFirstResponder];
    [scrollView insertSubview:tf aboveSubview:textField];
    if(isFirstResponder) {
        [tf becomeFirstResponder];
    }

    [textField removeFromSuperview];
    self.password = tf;

    if([text length]) {
        if(isFirstResponder) {
            // http://stackoverflow.com/questions/1317929/insert-string-at-cursor-position-of-uitextfield
            // Get a reference to the system pasteboard
            UIPasteboard* lPasteBoard = [UIPasteboard generalPasteboard];
            // Save the current pasteboard contents so we can restore them later
            NSArray* lPasteBoardItems = [lPasteBoard.items copy];
            // Update the system pasteboard with my string
            lPasteBoard.string = text;
            // Paste the pasteboard contents at current cursor location
            [tf paste:self];
            // Restore original pasteboard contents
            lPasteBoard.items = lPasteBoardItems;
            [lPasteBoardItems release];
        } else {
            tf.text = text;
        }
    }
} else {
    textField.secureTextEntry = NO;
}


With iOS, you should never try to "hack" stuff, if the behavior you want is not provided by the framework, change your mind !

First its easier ^^, second the user will not be responsive to this, then you never know if the next iOS update will break it or not, so it can be dangerous for your application.

"You want the user sees the password he is taping on a secured textfield", you can display a UILabel in the bottom instead ? Or a confirmation Alert box with the clear password ?


Swift version of Sandy's solution.

if #available(iOS 9.2, *) {
    passwordTextField.secureTextEntry = !passwordTextField.secureTextEntry
}
else {
    let wasFirstResponder = passwordTextField.isFirstResponder()
    if wasFirstResponder {
        passwordTextField.resignFirstResponder()
    }

    passwordTextField.secureTextEntry = !passwordTextField.secureTextEntry
    if wasFirstResponder {
        passwordTextField.becomeFirstResponder()
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消