I want to check if a user backspaces a character in a textView if there are any of that same character connecting it for it to delete them all...
For example if the character I'm checking for is "e" I have the text "easy heeeeeello" and the user starts hitting backspace it will become:
easy heeeeeello -> easy heeeeeell -> easy heeeeeel -> easy heeeeee -> easy h
The code should detect that a backspace was pressed.
Then it will detect which text is going to be deleted, and if that text is a character (in our case "e") it will check if there are more "e"s touching that "e" creating a strand of "e"s and delete them all.
Can you help me?
OK, so I wrote this code, and it works for me
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if([text isEqualToString:#""]) {
//Detected backspace character as the new character is #"" meaning something will be deleted
char toDelete = [textView.text characterAtIndex:range.location];
int duplicateCharCount = 0;
for(int i =range.location-1; i>=0; i--) {
if([textView.text characterAtIndex:i] == toDelete) {
duplicateCharCount++;
} else {
break;
}
}
NSRange newRange = NSMakeRange(0, range.location - duplicateCharCount);
[textView setText:[textView.text substringWithRange:newRange]];
return NO;
} else {
return YES;
}
}
I know its not the best implementation, but now you know how to proceed
Hope this helps
Kind of fun, so I wrote the code just now.
The code works.
First, we should set the UITextView's delegate and respond to .
textView:shouldChangeTextInRange:replacementText:
According to the document,
If the user presses the Delete key, the length of the range is 1 and an empty string object replaces that single character.
So the code comes below :
#pragma mark -
#pragma mark - UITextView Delegate
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
static NSString *suffix = #"e";
if (range.length == 1 && [text length] == 0) {
// The user presses the Delete key.
NSString *currentText = [textView.text substringToIndex:range.location+1];
NSString *appendingText = [textView.text substringFromIndex:range.location+1];
if ([currentText hasSuffix:suffix]) {
NSRange range = [self inverseRangeOfString:currentText withSuffix:suffix];
currentText = [currentText stringByReplacingCharactersInRange:range withString:#""];
textView.text = [currentText stringByAppendingString:appendingText];
return NO;
}
}
return YES;
}
- (NSRange)inverseRangeOfString:(NSString *)str withSuffix:(NSString *)suffix
{
int length = [str length];
int lastIndex = length - 1;
int cnt = 0;
for (; lastIndex >= 0; --lastIndex) {
NSString *subStr = [str substringFromIndex:lastIndex];
if ([subStr hasPrefix:suffix]) {
cnt++;
} else {
break;
}
}
NSRange range = (NSRange){++lastIndex, cnt};
return range;
}
Related
I want to do the customized autocompletion for UITextView...
For example:
If the user starts to type "Busines" I would like to suggest "Business1", "Business2", , so that the user can select any one of the 2 suggestions or choose to type a new one.
All the custom word suggestion will be in the array...
How can I achieve this ??
Is completionsForPartialWordRange:inString:language: something that I can use.. How can I pass the values in the array???
You can try this code.
- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring
{
[autocomplete_array removeAllObjects];
for(int i=0;i<[your_main_array count];i++)
{
NSString *curString = [your_main_array objectAtIndex:i];
curString = [curString lowercaseString];
substring = [substring lowercaseString];
if ([curString rangeOfString:substring].location == NSNotFound)
{}
else
{
[autocomplete_array addObject:curString]
}
}
[autocompletedisplayTableView reloadData];
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if( textView == your_TextView)
{
[your_TextView resignFirstResponder];
autocompletedisplayTableView.hidden = NO;
NSString *substring = [NSString stringWithString:your_TextView.text];
substring = [substring stringByReplacingCharactersInRange:range withString:string];
[self searchAutocompleteEntriesWithSubstring:substring];
return YES;
}
}
your_main_array : will be your original array which you'll load from web service.
autocomplete_array : will be the array you'll get after the searching process is finished.
When user search you'll have to pass autocomplete_array to your UITableView
I am getting Phone card number form user in UI text field. The format of number is like
123-4567-890
I want that as user types 123 automatically - is inserted in UITextField same after 4567 - and so on.
I Did it using following code in UITextField delegate method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;
{
NSLog(#"***** %d",textField.text.length);
if(textField.text.length == 3)
{
textField.text = [textField.text stringByAppendingString:#"-"];
}
return YES;
}
But the Problem raised while clear the text, When we start clearing.
Last 3 digits 890 clears and then - addded, we cleared it and again added and soooo on so clearing stop at
We clear all the text at a time using
textField.clearButtonMode = UITextFieldViewModeWhileEditing; //To clear all text at a time
But our requirement is user must delete one character at a time.
How to achieve it?
During clearing replacementString should be empty #"". So replacement string should be checked also in addition to length check. Like this:
if (textField.text.length == 3 && ![string isEqualToString:#""]) {
// append -
}
USE: I have seen this somewhere in this forum, It worked for me
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *filter = #"###-####-###";
if(!filter) return YES;
NSString *changedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if(range.length == 1 && string.length < range.length && [[textField.text substringWithRange:range] rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:#"0123456789"]].location == NSNotFound)
{
NSInteger location = changedString.length-1;
if(location > 0)
{
for(; location > 0; location--)
{
if(isdigit([changedString characterAtIndex:location]))
break;
}
changedString = [changedString substringToIndex:location];
}
}
textField.text = filteredStringFromStringWithFilter(changedString, filter);
return NO;
}
NSString *filteredStringFromStringWithFilter(NSString *string, NSString *filter)
{
NSUInteger onOriginal = 0, onFilter = 0, onOutput = 0;
char outputString[([filter length])];
BOOL done = NO;
while(onFilter < [filter length] && !done)
{
char filterChar = [filter characterAtIndex:onFilter];
char originalChar = onOriginal >= string.length ? '\0' : [string characterAtIndex:onOriginal];
switch (filterChar) {
case '#':
if(originalChar=='\0')
{
done = YES;
break;
}
if(isdigit(originalChar))
{
outputString[onOutput] = originalChar;
onOriginal++;
onFilter++;
onOutput++;
}
else
{
onOriginal++;
}
break;
default:
outputString[onOutput] = filterChar;
onOutput++;
onFilter++;
if(originalChar == filterChar)
onOriginal++;
break;
}
}
outputString[onOutput] = '\0';
return [NSString stringWithUTF8String:outputString];
}
I just followed a tut on making a conversion app. It was good, but I wanted to expand on it. The tut has you input a value in for Fahrenheit and then converts to Celsius. Pretty basic. So I wanted to add a Kelvin conversion as well. But the code only let you plug in a number for the Fahrenheit. So after adding the Kelvin text field, I wanted to check to see which text box had text in it. So I used the following code:
- (IBAction)convert:(id)sender
{
if ([fahrenheit isFirstResponder])
{
float x = [[fahrenheit text] floatValue];
float y = (x - 32.0f) * (5.0f/9.0f); //celcius
float z = y + 273.15f; //kelvin
[celcius setText:[NSString stringWithFormat:#"%3.2f" , y]];
[kelvin setText:[NSString stringWithFormat:#"%3.2f" , z]];
[fahrenheit resignFirstResponder];
} else if ([celcius isFirstResponder])
{
float x = [[celcius text] floatValue];
float y = 32.0f + ((9.0f/5.0f) * x); //farenheit
float z = x + 273.12f; //kelvin
[fahrenheit setText:[NSString stringWithFormat:#"%3.2f" , y]];
[kelvin setText:[NSString stringWithFormat:#"%3.2f" , z]];
[celcius resignFirstResponder];
}else if ([kelvin isFirstResponder])
{
float x = [[kelvin text] floatValue];
float y = x - 273.15f; //celcius
float z = 32.0f + ((9.0f/5.0f) * y); //farenheit
[celcius setText:[NSString stringWithFormat:#"%3.2f" , y]];
[fahrenheit setText:[NSString stringWithFormat:#"%3.2f" , z]];
[kelvin resignFirstResponder];
}
}
This allowed me to input a number in any text field and then convert. But then I decided to dismiss the keyboard. My code said to resignFirstResponder. But then the convert action did not work because now there was no first responder. Any clues as to how I can check which text box has text in it, and then do the conversions? Thanks in advance for any help.
if( [textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] != nil && [textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] != #"" )
{
// text field has text
// get text without white space
NSString * textWithoutWhiteSpace = [textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
This is for checking textView is empty or not:-
if([textView.text isEqualToString:#""])
{
//textView is Empty
}
else
{
//textView has text
}
If you want to check it for white space as well, first remove white spaces from string then check ... like this -
NSString *trimmedString = [tV.text stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if([trimmedString isEqualToString:#""])
{
NSLog(#"textView is empty");
}
else
{
NSLog(#"textView has some value");
}
Just use the hasText method.
Example:
if(_yourTextField.hasText)
{
// Do something.
}
if(textView.text.length > 0)
{
//text present
}
else
{
//no text
}
Better solution is make all conversions on the fly, add new action to all textFields
[textField addTarget:self action:#selector(textChanged:) forControlEvents:UIControlEventEditingChanged];
Then in method textChanged: do something like this:
- (void) textChanged:(UITextField *)tf {
if (tf.text.floatValue > 0) {
if (tf == fahrenheit) {
//Do convertion for fahrenheit
}
.
.
.
//etc.
}
}
On response to Meno's answer
DO NOT USE != #""
this check for pointer equality vs String equality
use:
[string isEqualToString:#""];
If you want to know it DURING input, and probably performs actions based on this info, you shall use:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
There were a few problems in some of the other answers, like they didn't use isEqualToString, and they superfluously removed potential characters from a string that we are only interested in if it is nil or not.
I don't have enough reputation to comment, so I am posting this as an answer.
For a similar issue, I used this to check each textfield that I needed to check for being empty:
- (BOOL) isEmpty:(UITextField*) field
{
if (!(field.text) ||
([[field.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString: #""]))
{
return YES;
}
else
{
return NO;
}
}
If you need an NSString with white space removed:
NSString *nonWhiteSpaceString = [textfield.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
Then you could use the length as a boolean:
BOOL textFieldHasText = nonWhiteSpaceString.length;
I'm currently formatting my a textfield in xcode, every 5th character I add a hyphen.
However I'm having alot of trouble I am currently wanting to check my textfields.text.length then once the length reaches 23 characters the submit button is press-able. So far this works where I have trouble is say if the user enters 23 characters and the button is press-able if the user decided to go back and delete one character there is nothing to update the new text length as I don't know how to catch the delete button of the numberpad... Dose anyone know how to do this?
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *separator = #"-";
int seperatorInterval = 5;
NSString *originalString = [regTextField.text stringByReplacingOccurrencesOfString:separator withString:#""];
if (![originalString isEqualToString:#""] && ![string isEqualToString:#""]) {
NSString *lastChar = [regTextField.text substringFromIndex:[regTextField.text length] - 1];
int modulus = [originalString length] % seperatorInterval;
[self validateTextFields];
if (![lastChar isEqualToString:separator] && modulus == 0) {
regTextField.text = [regTextField.text stringByAppendingString:separator];
}
}
[self validateTextFields];
return YES;
}
-(IBAction) validateTextFields {
if (regTextField.text.length >= 22){
[submitButton setEnabled:YES]; //enables submitButton
}
else {
[submitButton setEnabled:NO]; //disables submitButton
}
}
Try something like this:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
// Any new character added is passed in as the "text" parameter
if (!([text isEqualToString:#""] && range.length == 1) && [textView.text length] >=140 ) {
return NO;
}
// For any other character return TRUE so that the text gets added to the view
return YES;
}
Where the block:
([text isEqualToString:#""] && range.length == 1)
Is the check for the backspace.
Capturing the backspace on the Number Pad Keyboard
In my application i need to allow users input only numbers.
How can i allow UITextField to receive only numbers from user?
The characters in this examples are allowed, so if you dont want the user to use a character, exclude it from myCharSet.
- (BOOL)textField:(UITextField *)theTextField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:#"0123456789"];
for (int i = 0; i < [string length]; i++) {
unichar c = [string characterAtIndex:i];
if (![myCharSet characterIsMember:c]) {
return NO;
}
}
return YES;
}
I prefer the following solution that actually prevents any any input except from numbers and backspace. Backspace for some reason is represented by an empty string and could not be used unless empty string returns YES. I also popup an alert view when the user enters a character other that numbers.
- (BOOL)textField:(UITextField *)theTextField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (string.length == 0) {
return YES;
}
NSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:#"0123456789"];
for (int i = 0; i < [string length]; i++) {
unichar c = [string characterAtIndex:i];
if ([myCharSet characterIsMember:c]) {
return YES;
}
}
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Invalid Input" message:#"Only numbers are allowed for participant number." delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[av show];
return NO;
}
This is perhaps the cleanest, simplest solution to allow only positive or negative numbers. This also allows backspace.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
NSCharacterSet *allowedCharacters = [NSCharacterSet characterSetWithCharactersInString:#"-0123456789"];
if([string rangeOfCharacterFromSet:allowedCharacters.invertedSet].location == NSNotFound){
return YES;
}
return NO;
}
One thing you can do is to show the numbers key pad and beside text field or some where else add a dynamic button to hide the keyboard.
you guys might flame.. but this worked for me.. only numbers (including negatives), and backspace.
NSCharacterSet *validCharSet;
if (range.location == 0)
validCharSet = [NSCharacterSet characterSetWithCharactersInString:#"0123456789-."];
else
validCharSet = [NSCharacterSet characterSetWithCharactersInString:#"0123456789."];
if ([[string stringByTrimmingCharactersInSet:validCharSet] length] > 0 ) return NO; //not allowable char
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber* candidateNumber;
NSString* candidateString = [textField.text stringByReplacingCharactersInRange:range withString:string];
range = NSMakeRange(0, [candidateString length]);
[numberFormatter getObjectValue:&candidateNumber forString:candidateString range:&range error:nil];
if (candidateNumber == nil ) {
if (candidateString.length <= 1)
return YES;
else
return NO;
}
return YES;
Here is my solution applying algebra of sets with the method isSupersetOfSet: This also doesn't allow pasting text with invalid characters:
- (BOOL)textField:(UITextField *)theTextField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (string.length == 0 || [_numericCharSet isSupersetOfSet:[NSCharacterSet characterSetWithCharactersInString:string]]) {
return YES;
}
else {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Invalid Input"
message:#"Only numeric input allowed."
delegate:self
cancelButtonTitle:#"Close"
otherButtonTitles:nil];
[av show];
return NO;
}
}
Note: according to Apple Developer Library, It's preferable cache the static NSCharacterSet than to create it again and again (here _numericCharSet).
However I prefer to let the user to input any character and validate the value in the method textFieldShouldEndEditing: called when the textField tries to resign first responder.
In this manner the user can paste any text (maybe composed with a mix of letters and numbers) and tidy up it in my textFields. The users do not like to see limited their actions.
In Swift
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField.tag == 2 { //your textField
let invalid = NSCharacterSet(charactersInString: "aeiou") //characters to block
if let range = string.rangeOfCharacterFromSet(invalid) {
return false
}
}
return true
}
Here is a swift example
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
var disabledCharacters:NSCharacterSet = NSCharacterSet(charactersInString: "0123456789")
for (var i:Int = 0; i < count(string); ++i) {
var c = (string as NSString).characterAtIndex(i)
if (disabledCharacters.characterIsMember(c)) {
println("Can't use that character dude :/")
return false
}
}
return true
}
Don't forget to add UITextFieldDelegate to your UIViewController as well.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
char *x = (char*)[string UTF8String];
//NSLog(#"char index is %i",x[0]);
if([string isEqualToString:#"-"] || [string isEqualToString:#"("] || [string isEqualToString:#")"] || [string isEqualToString:#"0"] || [string isEqualToString:#"1"] || [string isEqualToString:#"2"] || [string isEqualToString:#"3"] || [string isEqualToString:#"4"] || [string isEqualToString:#"5"] || [string isEqualToString:#"6"] || [string isEqualToString:#"7"] || [string isEqualToString:#"8"] || [string isEqualToString:#"9"] || x[0]==0 || [string isEqualToString:#" "]) {
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 14) ? NO : YES;
} else {
return NO;
}
}
This thread is a little old, but for the sake of reference I am going to leave a solution in swift 3. This solution will combine decimalDigits and the actual decimal. You can put together whatever combination you'd like, but for my case this is what the requirements were.
// instantiate a mutable character set
let characterSet = NSMutableCharacterSet()
// assign the needed character set
characterSet.formUnion(with: NSCharacterSet.decimalDigits)
// only need the decimal character added to the character set
characterSet.addCharacters(in: ".")
// invert and return false if it's anything other than what we're looking for
if string.rangeOfCharacter(from: characterSet.inverted) != nil {
return false
}