UITextField auto-delete - ios

When a user holds down the delete key for a certain amount of time, the UITextField begins deleting multiple characters at once. I'm trying to create a UITextField that has a # as the first character. This # should never be deleted. The code below works to prevent the user from deleting the # accept when the user types in many characters, and then proceeds to hold down the delete key until UITextField deletes multiple characters at once. The user is then able to delete all characters from the UITextField despite the logic below. How can this be?
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSUInteger newLength = [textField.text length] + [string length] - range.length;
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newLength < 1)
return NO;
else if (newString.length == 0)
return NO;
return (newLength > 30) ? NO : YES;
}

Perhaps something like this would be better:
- (void) textViewDidChange:(UITextView *)textView {
if (![textView.text hasPrefix:#"#"]) {
textView.text = [NSString stringWithFormat:#"#%#", textView.text];
}
}
This way, at any point, if your text view doesn't have a '#' as a prefix, this puts one in. Otherwise, if the user types 10 characters, then goes back and erases the '#' the system won't recognize it, or if they highlight all of the text and erase it. If later code depends on the '#' char, I'd say this is probably more reliable.

I'm not sure if the shouldChangeCharactersInRange method is technically allowed to modify the text field directly, but give this a try and let me know how it goes.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if ( newString.length > 30 )
return( NO );
if ( newString.length < 1 )
newString = #"#";
textField.text = newString; // I'll change the string myself thanks
return( NO ); // string's already changed, don't change it again
}

Related

How to force the user to stop typing in UITextField but allow backspacing

I have a UITextField that my user uses to type out tags. I need to be able to at a very specific time - stop my user from continuing to type in the keyboard that is presented by default with UITextFields. However, I need my user to still be allowed to hit the backspace button on the iOS keyboard so that they can try and type a word that will fit.
A couple VERY important things to keep in mind:
The UITextField should not be frozen due to a maximum amount of characters, because I am determining when the textField should be frozen based off of UIViews that I am adding to the screen for the tags
The user still should be able to backspace
The keyboard should not be dismissed
The textField should not be disabled
I have tried setting the textField.enabled = NO, but once again, I need to still be able to use the textField, I just simply need to freeze the typing and force the user to backspace, not allowing andy characters to be added to the textField.
You could try something like below
- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range
replacementString: (NSString*) string {
NSString *text = [textField.text stringByReplacingCharactersInRange:range withString: string];
if([text length] > MAX_LENGTH)
return NO;
else
return YES;
}
EDIT1:
To this delegate method get called, set the textfield's delegate.
In this case, you can use it as
yourTextField.delegate = self;
In your viewWillAppear/viewDidAppear.
Try this
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Prevent crashing undo bug – see note below.
if(range.length + range.location > textField.text.length)
{
return NO;
}
NSUInteger newLength = [textField.text length] + [string length] - range.length;
if(textField == self.yourTextField)
{
return newLength <= CHARACTER_LIMIT;
}
else
{
return YES;
}
}
Try this, Back space will work even if the textfield pre populated with more than MAX_Length text
- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range
replacementString: (NSString*) string {
NSString *text = [textField.text stringByReplacingCharactersInRange:range withString: string];
NSString *text = [textField.text stringByReplacingCharactersInRange:range withString: string];
if([text length] > MAX_LENGTH && string.length>0)
return NO;
else
return YES;
}

Restrict user to enter abuse words in text view

I am developing a application where user has to enter only holy words. I want user to be restricted not to enter the abuse or adult word.
I have a big list of adult or abuse words whenever user will enter that word it should delete it automatically.
Any help will be appreciated.
You are probably using UITextField so you should look after forbidden words after text has changed:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSRange spaceRange = [newString rangeOfString:#" "];
if (spaceRange.location != NSNotFound) { // it's a new word
newString = [self stringWithoutForbiddenWords:newString];
}
textField.text = newString;
return NO; // we set the textField text manually
}
- (NSString *)stringWithoutForbiddenWords:(NSString *)string {
for (NSString *forbiddenWord in self.forbiddenWords) {
NSRange forbiddenWordRange = [string rangeOfString:forbiddenWord];
if (forbiddenWordRange.location != NSNotFound) {
// remove the forbidden word
string = [string stringByReplacingOccurrencesOfString:forbiddenWord withString:#""];
}
}
return string;
}
Don't forget to set you UITextField delegate.
Its a very simple logic , by the way only "Holy Word" seems very funny I hope you meant non-abusive words.
So to restrict abusive words, first make an Array and store all the abusive words in that.
then in textView shouldChangeTextInRange: check whenever user press " space.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if([string isEqualToString:#" "])
{
//now iterate the whole string and find whether any word contains any value from your Abusive words Array and replace the word with blank space or *
}

Adding a constant country code at beginning of UITextField

I have a UITextField that the user require to enter a phone number into it.
This is how it looks like right now:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// Auto-add hyphen before appending 4rd or 7th digit
//
//
if (range.length == 0 && (range.location == 3 || range.location == 7))
{
textField.text = [NSString stringWithFormat:#"%#-%#", textField.text, string];
return NO;
}
// Delete hyphen when deleting its trailing digit
//
//
if (range.length == 1 && (range.location == 4 || range.location == 8))
{
range.location--;
range.length = 2;
textField.text = [textField.text stringByReplacingCharactersInRange:range withString:#""];
return NO;
}
// Prevent crashing undo bug – see note below.
//
//
if (range.length + range.location > textField.text.length)
{
return NO;
}
// Limit text field characters
//
//
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 12) ? NO : YES;
}
After the 3rd digit, I'm adding a hyphen and than again. What I'm trying to achieve here is to add a country code as constant at start of the UITextField and that the user will not be able to remove it. Lets say USA country code, then the UITextField text will look like that at start +1- and then after writing the full number it will look like that: +1-600-242-252
How can I do that?
Thanks in advance!
This answer assumes a starting country code string which includes the hyphen at the end, ex: self.countryCode = #"+1-";. The text field should initially contain "+1-".
I've made my answer way more comprehensive than your original intent because it handles many use cases you've overlooked, for example copy and paste operations with multiple characters, inappropriate hyphen deletion, inappropriate hyphen addition, mid-line insertion, etc. It's still not perfect though because your original answer was unspecific in some ways... For example, if you specify that the user should only be able to enter digits, the code can be much cleaner.
The below implementation is described line by line in the comments included throughout.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// Combine the new text with the old
NSMutableString *combinedText = [[textField.text stringByReplacingCharactersInRange:range withString:[NSString stringWithFormat:#"%#", string]] mutableCopy];
// If the user deletes part of the country code or tries
// to edit it in any way, don't allow it
if (combinedText.length < self.countryCode.length ||
![combinedText hasPrefix:self.countryCode]) {
return NO;
}
// Limit text field characters to 12
if (combinedText.length > self.countryCode.length + 12) {
return NO;
}
// If the user tries to add a hyphen where there's supposed
// to be a hyphen, allow them to do so.
if ([string isEqualToString:#"-"] &&
(range.location == self.countryCode.length + 3 ||
range.location == self.countryCode.length + 7)) {
return YES;
}
// Remove all the hyphens other than the one directly
// following the country code
[combinedText replaceOccurrencesOfString:#"-" withString:#"" options:0 range:NSMakeRange(self.countryCode.length, [combinedText length] - self.countryCode.length)];
// Auto-add the hyphens before the 4th and 7th digits
if (combinedText.length > self.countryCode.length + 3)
[combinedText insertString:#"-" atIndex:self.countryCode.length + 3];
if (combinedText.length > self.countryCode.length + 7)
[combinedText insertString:#"-" atIndex:self.countryCode.length + 7];
// Store the original cursor position
UITextPosition *pos = [textField selectedTextRange].start;
// Count up the original number of hyphens
NSUInteger originalNumberOfHyphens = [[textField.text componentsSeparatedByString:#"-"] count] - 1;
// Count up the new number of hyphens
NSUInteger newNumberOfHyphens = [[combinedText componentsSeparatedByString:#"-"] count] - 1;
// Create a cursor offset to reflect the difference
// in the number of hyphens
float offset = newNumberOfHyphens - originalNumberOfHyphens;
// Update the text field to contain the combined text
textField.text = combinedText;
// Update the cursor position appropriately
if (string.length > 0) {
UITextPosition* cursor = [textField positionFromPosition:[textField beginningOfDocument] offset:range.location + range.length + offset + string.length];
textField.selectedTextRange = [textField textRangeFromPosition:cursor toPosition:cursor];
} else {
UITextPosition* cursor = [textField positionFromPosition:pos inDirection:UITextLayoutDirectionLeft offset:1-offset];
textField.selectedTextRange = [textField textRangeFromPosition:cursor toPosition:cursor];
}
// No need to replace the string since it's already been done
return NO;
}
To keep a constant at the beginning, you basically want to check if the constant still exist in the proposed text. If it doesn't reject such edits.
You should not try to insert hyphens at specific editing steps. It's better to manipulate the whole string.
E.g.
test if string could be valid. i.e. starts with +1
remove all hyphens you previously added
reinsert all hyphens
In code this would look like this:
- (void)viewDidLoad {
[super viewDidLoad];
self.textField.text = #"+1"; // start with a +1 in the textField otherwise we can't change the field at all
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *proposedText = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (![proposedText hasPrefix:#"+1"]) {
// tried to remove the first +1
return NO;
}
NSString *formattedPhoneNumber = [proposedText substringFromIndex:2]; // without +1 prefix
NSString *unformattedPhoneNumber = [formattedPhoneNumber stringByReplacingOccurrencesOfString:#"-" withString:#""]; // without hypens
// start with the prefix
NSMutableString *newText = [NSMutableString stringWithString:#"+1"];
for (NSInteger i = 0; i < [unformattedPhoneNumber length]; i++) {
if (i % 3 == 0) {
// add a - every 3 characters. add one at the beginning as well
[newText appendString:#"-"];
}
// add each digit from the unformatted phonenumber
[newText appendString:[unformattedPhoneNumber substringWithRange:NSMakeRange(i, 1)]];
}
textField.text = newText;
return NO;
}
This is still a very naive implementation. It has a couple of problems, for example the cursor will always be at the end because we set text of the textField manually. So the user can't easily remove numbers in the middle of the string. Of course there are ways around this. selectedTextRange would be the property to use. And you can't really paste phone numbers into the field. And of course the user can't delete a hyphen.
Formatting while the user is typing tends to get complicated quickly because there are so many edge cases. But that should get you started.

Unable to use backspace key to delete a character from a textfield in iOS

I am implementing the following delegate method for UITextField:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *integerPart = [textField.text componentsSeparatedByString:#"."][0];
NSString *decimalPart = [textField.text componentsSeparatedByString:#"."][1];
if ([integerPart length] > 8 || [decimalPart length] > 5) {
return NO;//this clause is always called.
}
...
}
I am trying to limit the number of digits entered in the textField to 6. The problem I have is that if I enter a number with 6 digits after the decimal, and then try to press the backspace key on my device to delete the numbers, or need to make a correction inside the number, I'm unable to.
The reason is that whenever it comes to this point in my code, it notices that I have already entered 6 digits after the decimal (which is correct), and thus, nullifies my backspace key entry. How do I maintain this limit of 6 digits after the decimal place, AND allow for editing of the number after reaching this limit?
I haven't had a chance to test this, but according to this answer, string should be empty when a backspace is entered (which makes sense). So you should be able to do this.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Always allow a backspace
if ([string isEqualToString:#""]) {
return YES;
}
// Otherwise check lengths
NSString *integerPart = [textField.text componentsSeparatedByString:#"."][0];
NSString *decimalPart = [textField.text componentsSeparatedByString:#"."][1];
if ([integerPart length] > 8 || [decimalPart length] > 5) {
return NO;//this clause is always called.
}
return YES;
}
//Construct the new string with new input
NSString* newText = [textField.text stringByReplacingCharactersInRange:range
withString:text];
NSString *integerPart = [newText componentsSeparatedByString:#"."][0];
NSString *decimalPart = [newText componentsSeparatedByString:#"."][1];
if ([integerPart length] > 8 || [decimalPart length] > 5) {
return NO;//this clause is always called.
}
I believe this is what you need. This will construct the new string that will be displayed in the textfield and you can evaluate that string.

Maximum number of characters in UITextField allowing deleting?

I have a UITextField which I'd like to have a 30 character limit. I'm doing that in this way:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 30) ? NO : YES;
}
Currently, if I enter two duplicate entries into the table these text fields are filling, our server appends _1234 onto the end, four random numbers, to prevent duplicate names. So, for instance, if I enter abc, then abc again, the second one will be named abc_8252.
If I enter two duplicate 30-character names, the _9392 is still appended, making an entry of 35 characters, and the code above falls apart because the replacementString will never bring it down under 30 characters.
What I'd like to do is, in the event that the string is > 30 characters, only allow the backspace key, so that they can bring it back down, but not make it any longer, and once it drops below 30, won't be able to go back above it again. Can I do that?
Fixed it like so:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
// Allow a backspace always, in case we went over 30 due to the _1234 appending.
const char *_char = [string cStringUsingEncoding:NSUTF8StringEncoding];
int isBackSpace = strcmp(_char, "\b");
if (isBackSpace == -8) {
return YES;
}
// If it's not a backspace, allow it if we're still under 30 chars.
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 30) ? NO : YES;
}

Resources