Reputation: 891
I have a button that toggles between Show/Hide mode (i.e. toggles a UITextField between secureTextEntry NO and YES). The purpose of which is to allow the user to see the password they are entering.
I followed the example (with the highest number of votes) here: UITextField secureTextEntry - works going from YES to NO, but changing back to YES has no effect
However, when I set secureTextEntry to NO, any text that was written there ends up with a space at the end. This does not seem to be a problem when setting secureTextEntry to YES.
For example, if I enter the text "mypassword" while setSecureTextEntry is set to NO, and then switch it to YES, the user will see ********** (10 dots), which is correct. If I setSecureTextEntry to NO, the user will see "mypassword " (with a space at the end, or at least, the cursor moved one space to the right).
Important note: In the debugger, the string value of text appears without the trailing space, like this:
(lldb) expr self.passwordText.text
(NSString *) $0 = 0x1d8450e0 @"mypassword"
I have tried trimming whitespace (per avoid middle whitespace in UITextField), but it has had no effect.
Upvotes: 35
Views: 10299
Reputation: 11
I had a similar issue and realized it was because I was updating the text before setting the secureTextEntry property. It makes sense that the textField would draw out the caret at the location it'd be at if it were using secureTextEntry.
I did not read the entire problem nor did I visit the solution linked by OP, but in case someone else has the same issue as me:
Try updating your text after setting the secureTextEntry property.
Upvotes: 0
Reputation: 109
Swift UITextField extension:
extension UITextField {
func toggleSecureEntry() {
let wasFirstResponder = isFirstResponder
if wasFirstResponder { resignFirstResponder() }
isSecureTextEntry.toggle()
if wasFirstResponder { becomeFirstResponder() }
}
}
Setting textField.text solution also works in some situations but not for my need (Custom font with two text fields. Caused font changes and glitches on runtime.) Adding here too.
func toggleSecureEntry() {
isSecureTextEntry.toggle()
let originalText = text
text = nil
text = originalText
}
Upvotes: 1
Reputation: 12687
Bug is on radar, there is explanation of workaround also: http://www.openradar.me/38465011
Here is cut of temporary workaround how to natively update caret (cursor) position.
// 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)
}
Upvotes: 0
Reputation: 838
i've just encounter this case and finally solved this problem.
works on Latest iOS SDK, iOS 8.1
First of all, there is no trailing space at all.
The dot(shown in SecureEntry) character and normal character have different width and after you toggle isSecureEntry switch, the cursor didn't refresh it's position.
so i use this workaround to solved this problem.
- (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;
}
Swift 3+
// Workaround to refresh cursor
let currentText: String = self.userPassword.text!
self.userPassword.text = "";
self.userPassword.text = currentText
hope it helps!
Upvotes: 42
Reputation: 1765
I'm using this, Works fine.
[self.yourTextField becomeFirstResponder];
Upvotes: 0
Reputation: 1
Here is the solution:
- (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];
}
Upvotes: 0
Reputation: 1
Everytime the text is set in the UITextField, the cursor postition is updated
So I used this code
partial void btnShowPassword_ToutchUpInside (UIButton sender)
{
if (TxtPassword.SecureTextEntry == true) {
TxtPassword.SecureTextEntry = false;
TxtPassword.Text = TxtPassword.Text;
} else {
TxtPassword.SecureTextEntry = true;
}
}
Upvotes: 0
Reputation: 14857
I have a clean solution not going dirty with text
property of UITextField
.
Wrap them in this style.
[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.
Upvotes: 8
Reputation: 1145
You can fix it like this:
NSString *currentText = self.textfield.text;
self.textfield.text = @"";
self.textfield.text = currentText;
Upvotes: 4
Reputation: 21
To get the cursor to reposition correctly, setting the font attributes seemed to do the trick for me.
// Hack to update cursor position
self.passwordTf.defaultTextAttributes = @{NSFontAttributeName: textFieldFont, NSForegroundColorAttributeName: textFieldColor};
// Change secure entry
self.passwordTf.secureTextEntry = !self.passwordTf.secureTextEntry;
Tested on iOS8, iOS9. Hope it helps!
Upvotes: 0
Reputation: 541
PRE-iOS-8.0 (dated solution)... In your button's action method (toggling between secureTextEntry
YES and NO), simply set UITextField
's text
property to its current text
value. Although this may seem redundant and a bit like a hack, this will redraw the cursor in the right position. Here's an example of what your button's action method should look like now...
-(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... As of iOS 8.0, it appears that UITextField
's text
setter no longer redraws the cursor when called with a string equal to its current string value. Now, we need to take this a step further and actually change the text
value before resetting it again. Replace the above setText:
line with something like these lines.
// * 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
Upvotes: 13
Reputation: 13843
This Works in my case
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];
Upvotes: 1
Reputation: 10801
In order to work around this bug in iOS you can simply do the following (works for any iOS version):
- (IBAction)toggleSecureTextEntry:(UIButton *)button
{
self.textField.secureTextEntry = !self.textField.secureTextEntry;
NSString *originalText = self.textField.text;
self.textField.text = nil;
self.textField.text = originalText;
}
Upvotes: 6
Reputation: 391
This is another possibility to solve this issue, where self.passwordText is the UITextField:
if (self.passwordText.isFirstResponder) {
[self.passwordText resignFirstResponder];
[self.passwordText becomeFirstResponder];
}
Upvotes: 2
Reputation: 129
When we change a textfield.secureTextEntry property, the caret position is not updated. To fix this, the code below used to work before IOS 8:
pwdTextField.text = pwdTextField.text
Now it doesn't. It seems IOS 8 detects the new value equals old value and does nothing. So to make it work again we have to actually change the value. Here is the swift version that works for me.
let str = pwdTextField.text
pwdTextField.text = str + " "
pwdTextField.text = str
Upvotes: 2
Reputation: 118
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]];
This is what I used for iOS 8
Upvotes: 3
Reputation: 379
This work for me on 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;
}
Upvotes: 3
Reputation: 891
It appears that the second solution in the referenced link, when implemented, has the desired behavior of not adding an extra space:
https://stackoverflow.com/a/8495888/738190
Upvotes: 1