I\'m stuck with a problem of changing language for the password field. In my application I need to enter login/password in hebrew with no care of current locale. When I try
Didn't find a solution. Had to make this snippet:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSString *pass = password;
pass = [pass stringByReplacingCharactersInRange:range withString:string];
[password release];
password = nil;
password = [[NSString stringWithString:pass] retain];
[self hideTextInTextFieldExceptOne:string];
[self performSelector:@selector(hideTextInTextField) withObject:self afterDelay:1.0];
return NO;
}
- (void)hideTextInTextFieldExceptOne:(NSString *)string
{
int lenght = [passwordTextField.text length];
for (int i = 0; i < lenght-1; i++)
{
NSRange range = NSMakeRange(i, 1);
passwordTextField.text = [passwordTextField.text stringByReplacingCharactersInRange:range withString:@"*"];
}
}
- (void)hideTextInTextField
{
NSUInteger lenght = [passwordTextField.text length];
passwordTextField.text = @"";
for (int i = 0; i < lenght; i++)
{
passwordTextField.text = [passwordTextField.text stringByAppendingString:@"*"];
}
}
Optimized @lonlywolf answer to better performance code: Optimized only two methods where are cycles, which slowed down program when typing more than 30 symbols
- (void)hideTextInTextFieldExceptOne:(NSString *)string
{
int lenght = [passwordTextField.text length];
if (lenght -1 > 0) {
NSString *resultString = @"";
for (int i = 0; i < lenght-1; i++)
{
resultString = [resultString stringByAppendingFormat:@"*"];
}
NSRange range = NSMakeRange(0, lenght - 1);
passwordTextField.text = [fieldPassword.text stringByReplacingCharactersInRange:range withString:resultString];
}
}
- (void)hideTextInTextField
{
int lenght = [passwordTextField.text length];
if (lenght > 0) {
NSString *resultString = @"";
for (int i = 0; i < lenght; i++)
{
resultString = [resultString stringByAppendingFormat:@"*"];
}
passwordTextField.text = resultString;
}
}
Unfortunately, the solution posted here does not work for languages with composite characters (like Korean).
Languages like Korean (Hangul) has a composite character where each letter is composed of multiple symbols. For example, ‘ㅁ’, ‘ㅏ’ and ‘ㄴ’ are all individual characters, but when combined, it becomes ‘만’, which is treated as a single letter.
Here is a solution that works for all languages.
Place a UILabel on top of a UITextField. Set the UILabel's frame slightly smaller than the UITextField so that it sits inside the UITextField, yet still obscures UITextField's text. The textField: shouldChangeCharactersInRange:withReplacementString is called before the text is completed. This means we need to complete the text ourselves. This is harder to do with composite character languages like Korean. Instead, register for the UITextFieldTextDidChangeNotification which will be called after the new text appears on the UITextField.
@interface MKSecureTextField()<UITextFieldDelegate>
@property (nonatomic, strong) UITextField* textField;
@property (nonatomic, strong) UILabel* hideLabel;
@property (nonatomic, strong) NSTimer* hideTimer;
@property (nonatomic, strong) NSTimer* blinkTimer;
@end
@implementation MKSecureTextField
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
_textField.userInteractionEnabled = YES;
_textField.borderStyle = UITextBorderStyleRoundedRect;
_textField.font = [UIFont systemFontOfSize:14];
_textField.placeholder = @"enter text";
_textField.autocorrectionType = UITextAutocorrectionTypeNo;
_textField.keyboardType = UIKeyboardTypeDefault;
_textField.returnKeyType = UIReturnKeyDone;
_textField.clearButtonMode = UITextFieldViewModeWhileEditing;
_textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
_textField.delegate = self;
self.hideLabel = [[UILabel alloc] initWithFrame:CGRectMake(6, 5, frame.size.width-10, frame.size.height-12)];
_hideLabel.backgroundColor = [UIColor whiteColor];
[self addSubview:_textField];
[self addSubview:_hideLabel];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldDidChange:) name:UITextFieldTextDidChangeNotification object:nil];
}
return self;
}
When the UITextFieldTextDidChangeNotification is received, hide all but the last character. The hidden text will be programmatically set on the UILabel. Also, schedule a timer that will hide the last character. This timer is invalidated if more text is typed.
- (void)textFieldDidChange:(NSNotification*)notification
{
UITextField* textField = notification.object;
if (textField == _textField)
{
NSString* text = textField.text;
[self hideExceptLastCharacter:text];
[self.hideTimer invalidate];
self.hideTimer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(hideLastCharacter)
userInfo:nil
repeats:NO];
}
}
UILabel does not have a blinking cursor. So we emulate it by alternating between a '|' and a space. The blinking cursor is placed when the edit begins, and is removed when the edit ends.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if (_hideLabel.text == nil)
{
_hideLabel.text = @"|";
}
else
{
_hideLabel.text = [_hideLabel.text stringByAppendingString:@"|"];
}
self.blinkTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(blinkCursor) userInfo:nil repeats:YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
NSRange range;
range.location = _hideLabel.text.length - 1;
range.length = 1;
_hideLabel.text = [_hideLabel.text stringByReplacingCharactersInRange:range withString:@""];
[_blinkTimer invalidate];
}
- (void)blinkCursor
{
if (_hideLabel.text.length > 0)
{
static BOOL visible = YES;
NSRange range;
range.location = _hideLabel.text.length - 1;
range.length = 1;
if (visible)
{
_hideLabel.text = [_hideLabel.text stringByReplacingCharactersInRange:range withString:@" "];
}
else
{
_hideLabel.text = [_hideLabel.text stringByReplacingCharactersInRange:range withString:@"|"];
}
visible = !visible;
}
}
Characters except for the last one is hidden. This is set on the UILabel. The UITextField is left untouched.
- (void)hideExceptLastCharacter:(NSString*)string
{
int length = [_textField.text length];
NSString* s = @"";
for (int i = 0; i < length-1; i++)
{
s = [s stringByAppendingString:@"●"];
}
if (_textField.text.length > 0)
{
_hideLabel.text = [s stringByAppendingString:[_textField.text substringFromIndex:_textField.text.length-1]];
_hideLabel.text = [_hideLabel.text stringByAppendingString:@"|"];
}
else
{
_hideLabel.text = @"|";
}
}
- (void)hideLastCharacter
{
if (_hideLabel.text.length > 1)
{
NSRange range;
range.location = [_hideLabel.text length]-2;
range.length = 1;
_hideLabel.text = [_hideLabel.text stringByReplacingCharactersInRange:range withString:@"●"];
}
}
- (void)dealloc
{
[_hideTimer invalidate];
[_blinkTimer invalidate];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
See this Github project for reference.