How to verify input in UITextField (i.e., numeric input) - iphone

I am fairly new to iPhone development and I have what seems to be a simple question that I cannont figure out. How can I verify that a user input a number and a decimal into the text field?
I have tried many different things but the closes I can get only allows for numeric strings of the form: 5.34 or 23.89. I need it to allow numbers such as; 0.50 or 432.30, just something with a zero as the last input value. I attempted to check the string input by converting it to a float and then back to a string, but get the abovementioned results.
ANY help would be great (same with sample code!) THANKS!

I had a similar requirement and the solution ultimately turned out to be fairly trivial. Unfortunately a lot of the questions and answers related to this question are about validating or formatting numeric values, not controlling what a user could input.
The following implementation of the shouldChangeCharactersInRange delegate method is my solution. As always, RegularExpressions rock in this situation. RegExLib.com is an excellent source for useful RegEx's. I'm not a RegEx guru and always struggle a bit putting them together so any suggestions to improve it are welcome.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField == self.quantityTextField)
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSString *expression = #"^([0-9]+)?(\\.([0-9]{1,2})?)?$";
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression
options:NSRegularExpressionCaseInsensitive
error:&error];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:newString
options:0
range:NSMakeRange(0, [newString length])];
if (numberOfMatches == 0)
return NO;
}
return YES;
}
The above code allows the user to input these kinds of values: 1, 1.1, 1.11, .1, .11

The question that Dave DeLong was trying to post is 1320295 and looks very relevant.
This appears to be an old question, youve probably found your solution, Would be nice if you share it will all =)

You can cast the content of your textfield in float and format it by using the code below :
float myFloat = [myTextField floatValue];
NSString *formatString = [NSString stringWithFormat:#"%%1.%if", 2]; //2 decimals after point
NSString *resultString = [NSString stringWithFormat:formatString, myFloat];
If you want to allow only numeric values in your textfield, you can just reput the resultString in your textField so any not allowed text will be replaced by the formatted float value.
If the user puts "abc12.4" the result will be "0". So it would be better if you use the UITextFieldDelegate method :
textField:shouldChangeCharactersInRange:replacementString:
to check the last key tapped by user. You can just compare it to know if it's a numeric value or a point/comma.

Related

Search pattern in string using regex in obj-c

I'm working on a string pattern match algorithm. I use NSRegularExpression for finding the matches. For ex: I've to find all words starting with '#' in a string..
Currently I use the following regex function:
static NSRegularExpression *_searchTagRegularExpression;
static inline NSRegularExpression * SearchTagRegularExpression()
{
if (!_searchTagRegularExpression)
{
_searchTagRegularExpression = [[NSRegularExpression alloc]
initWithPattern:#"(?<!\\w)#([\\w\\._-]+)?"
options:NSRegularExpressionCaseInsensitive
error:nil];
}
return _searchTagRegularExpression;
}
and I use it as below:
NSString *searchString = #"Hi, #Hash1 #Hash2 #Hash3...";
NSRange searchStringRange = NSMakeRange(0, searchString.length);
NSRegularExpression *regexp = SearchTagRegularExpression();
[regexp enumerateMatchesInString:searchString
options:0
range:searchStringRange
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
{
// comes here for every match with range ( in this case thrice )
}];
This works properly. But i just want to know if this is the best way. suggest if there's any better alternative...
Actually your proposed pattern and implementation is quite good:
The pattern is quite precise with its use of the (fancy) zero-width negative look behind assertion to make sure you only match at the beginning of a word. It works correctly at the beginning of a string, for example.
The implementation reuses the regex object and avoids recompilation of the pattern.
If you wanted me to be nitpicking: You could drop the NSRegularExpressionCaseInsensitive option as your pattern does not use any parts that have a case.
What you do is a good way for sure. You can do this too,
for(NSString *match in [string componentSeperatedByString:#" "])
{
if([match hasPrefix:#"#"])
{
//do what you like.
}
}

iPhone Text Field Only One Input Type

I have some text fields on my project.
I want that input types of text fields are only one type. For example credit card number text field's input must be only decimals and user enter another type character, it will remove or not accept in the text field.
Sorry my bad english.
You have to use textField:shouldChangeCharactersInRange:replacementString: in textField's delegate, returning YES or NO for any change. You can analyze replacementString for forbidden characters and reject it if they are found.
you can declare like the below and use
#define LEGAL #"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz "
// allow only legal letters
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:LEGAL] invertedSet];
NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:#""];
return [string isEqualToString:filtered];
}
and we can use like this also
entryField.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
If you have created the UITextField by code, then you need to set as follows:
textField.keyboardType = UIKeyboardTypeNumberPad;

How do I identify the part of speech of a word within a NSString?

The app I'm currently working on requires me to determine the part of speech of a word in NSString.
So basically is there a library/database/class which you can access in Objective C which allows one to check if a single word (in the form of a NSString) is a noun, an adjective, an adverb or a verb?
Something along the lines of:
NSString *foo="cat";
if ([foo wordIsNoun]) {
//do something
};
On a similar but slightly unrelated note, is it possible to check if two NSString containing verbs of the same stem but different tense (ask, asking, asked, etc) have the same stem? It would be very useful as well.
You can do this with an NSLinguisticTagger! I've never used one before, but I hacked this together:
NSString *str = #"i have a cat";
NSLinguisticTagger *tagger = [[NSLinguisticTagger alloc] initWithTagSchemes:[NSArray arrayWithObject:NSLinguisticTagSchemeLexicalClass] options:~NSLinguisticTaggerOmitWords];
[tagger setString:str];
[tagger enumerateTagsInRange:NSMakeRange(0, [str length])
scheme:NSLinguisticTagSchemeLexicalClass
options:~NSLinguisticTaggerOmitWords
usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
NSLog(#"found: %# (%#)", [str substringWithRange:tokenRange], tag);
}];
[tagger release];
When you run this, it logs:
found: i (Pronoun)
found: have (Verb)
found: a (Determiner)
found: cat (Noun)
Note, however, that NSLinguisticTagger is only available on iOS 5+ (and Mac OS X 10.7+).

iPhone Objective C - How to remove URLs from an NSString

I am looking for an efficient way to replace URLs in an NSString (or NSMutableString) with the replacement word 'link', for example ...
#"This is a sample with **http://bitbanter.com** within the string and heres another **http://spikyorange.co.uk** for luck"
I would like this to become...
#"This is a sample with **'link'** within the string and heres another **'link'** for luck"
Ideally, I would like this to be some sort of method that accepts regular expressions, but, this needs to work on the iPhone, preferably without any libraries, or, I could be persuaded if the library was tiny.
Other features that would be handy, replace #"OMG" with #"Oh my God", but not when it's part of a word, i.e. #"DOOMGAME" shouldn't be touched.
Any suggestions appreciated.
Regards,
Rob.
This was actually quite a bit of fun to play with and hopefully the solution is somehow what you were looking for. This is flexible enough to cater not only for links, but also other patterns where you may want to replace a word for another using certain conditions:
I have commented most of the code so it should be pretty self explanatory. If not, feel free to leave a comment and I will try my best to help:
- (NSString*)replacePattern:(NSString*)pattern withReplacement:(NSString*)replacement forString:(NSString*)string usingCharacterSet:(NSCharacterSet*)characterSetOrNil
{
// Check if a NSCharacterSet has been provided, otherwise use our "default" one
if (!characterSetOrNil)
characterSetOrNil = [NSCharacterSet characterSetWithCharactersInString:#" !?,()]"];
// Create a mutable copy of the string supplied, setup all the default variables we'll need to use
NSMutableString *mutableString = [[[NSMutableString alloc] initWithString:string] autorelease];
NSString *beforePatternString = nil;
NSRange outputrange = NSMakeRange(0, 0);
// Check if the string contains the "pattern" you're looking for, otherwise simply return it.
NSRange containsPattern = [mutableString rangeOfString:pattern];
while (containsPattern.location != NSNotFound)
// Found the pattern, let's run with the changes
{
// Firstly, we grab the full string range
NSRange stringrange = NSMakeRange(0, [mutableString length]);
NSScanner *scanner = [[NSScanner alloc] initWithString:mutableString];
// Now we use NSScanner to scan UP TO the pattern provided
[scanner scanUpToString:pattern intoString:&beforePatternString];
// Check for nil here otherwise you will crash - you will get nil if the pattern is at the very beginning of the string
// outputrange represents the range of the string right BEFORE your pattern
// We need this to know where to start searching for our characterset (i.e. end of output range = beginning of our pattern)
if (beforePatternString != nil)
outputrange = [mutableString rangeOfString:beforePatternString];
// Search for any of the character sets supplied to know where to stop.
// i.e. for a URL you'd be looking at non-URL friendly characters, including spaces (this may need a bit more research for an exhaustive list)
NSRange characterAfterPatternRange = [mutableString rangeOfCharacterFromSet:characterSetOrNil options:NSLiteralSearch range:NSMakeRange(outputrange.length, stringrange.length-outputrange.length)];
// Check if the link is not at the very end of the string, in which case there will be no characters AFTER it so set the NSRage location to the end of the string (== it's length)
if (characterAfterPatternRange.location == NSNotFound)
characterAfterPatternRange.location = [mutableString length];
// Assign the pattern's start position and length, and then replace it with the pattern
NSInteger patternStartPosition = outputrange.length;
NSInteger patternLength = characterAfterPatternRange.location - outputrange.length;
[mutableString replaceCharactersInRange:NSMakeRange(patternStartPosition, patternLength) withString:replacement];
[scanner release];
// Reset containsPattern for new mutablestring and let the loop continue
containsPattern = [mutableString rangeOfString:pattern];
}
return [[mutableString copy] autorelease];
}
And to use your question as an example, here's how you could call it:
NSString *firstString = #"OMG!!!! this is the best convenience method ever, seriously! It even works with URLs like http://www.stackoverflow.com";
NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:#" !?,()]"];
NSString *returnedFirstString = [self replacePattern:#"OMG" withReplacement:#"Oh my God" forString:firstString usingCharacterSet:characterSet];
NSString *returnedSecondString = [self replacePattern:#"http://" withReplacement:#"LINK" forString:returnedFirstString usingCharacterSet:characterSet];
NSLog (#"Original string = %#\nFirst returned string = %#\nSecond returned string = %#", firstString, returnedFirstString, returnedSecondString);
I hope it helps!
Cheers,
Rog
As of iOS 4, NSRegularExpression is available. Amongst other things, you can enumerate all matches within a string via a block, allowing you to do whatever you want to each, or have the regular expression perform some kinds of substitution directly for you.
Direct string substitutions (like 'OMG' -> 'Oh my God') can be performed directly by an NSString, using -stringByReplacingOccurencesOfString:withString:, or replaceOccurrencesOfString:withString:options:range: if your string is mutable.

Ignoring certain strings when sorting an array

I’m making a languages application, and I have a long list of vocabulary relating to that language (German, in case anyone was interested). I have the functionality in my app to switch between sorting the tableview by German words, or by english words.
When I use the following:
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:type];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSArray *array = [[string componentsSeparatedByString:#"\n"] sortedArrayUsingSelector:#selector(compare:)];
it works absolutely perfectly - by which I mean, exactly as expected. What I would like to improve on this however, is that there are certain words, such as verbs or nouns, which are always preceded by prefixes, like “to”, as in “to do something”, or “the” in front of nouns. So what I would like to do is somehow exclude these from my sort, because otherwise I end up with all the verbs being sorted alphabetically under the “t” section in my array, which is not very user friendly.
I’ve looked through the Apple documentation about NSString and NSArray, as this is where the compare function is (unless I’m very much mistaken), and I haven’t found any way that makes sense to me. This is the first time I have done any data handling like this so I may be missing something simple, and so I would really appreciate some help.
Thanks very much
Michaeljvdw
You're on the right track. What you want to use instead of the (built-in) compare method is to write your own method, which can eliminate the "to" or "the" bits if they exist, and then use the existing compare method.
Your call would look something like this:
NSArray *array = [[string componentsSeparatedByString:#"\n"] sortedArrayUsingSelector:#selector(myCompare:)];
Using a custom category you give to NSString with the following methods:
// This method can be exposed in a header
- (NSComparisonResult)myCompare:(NSString*)aString
{
NSString* selfTrimmed = [self removeArticles];
NSString* aStringTrimmed = [s2 removeArticles];
return [self compare:aString];
}
// This method can be kept private in the .m implementation
- (NSString*)removeArticles
{
NSRange range = NSMakeRange(NSNotFound, 0);
if ([self hasPrefix:#"to "])
{
range = [self rangeOfString:#"to "];
}
else if ([self hasPrefix:#"the "])
{
range = [self rangeOfString:#"the "];
}
if (range.location != NSNotFound)
{
return [self substringFromIndex:range.length];
}
else
{
return self;
}
}
You might have some luck with localizedCompare: or localizedStandardCompare:, but I don't think that either of these will strip out articles and prepositions like you want. Instead, you will probably have to define a category on NSString that provides the specific style of sorting you're looking for:
#interface NSString (MySortAdditions)
- (NSComparisonResult)compareWithoutArticles:(NSString *)other;
#end
#implementation NSString (MySortAdditions)
- (NSComparisonResult)compareWithoutArticles:(NSString *)other {
NSMutableString *mutableSelf = [NSMutableString stringWithString:self];
[mutableSelf
replaceOccurrencesOfString:#"das"
withString:#""
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, [mutableSelf length])
];
...
// delete articles from 'other' too
NSCharacterSet *trimSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *trimmedSelf = [mutableSelf stringByTrimmingCharactersInSet:trimSet];
NSString *trimmedOther = ...;
return [trimmedSelf localizedCaseInsensitiveCompare:trimmedOther];
}
#end
You can then use #selector(compareWithoutArticles:) as your sort selector for NSArray.
First, don't use compare:. Use localizedCompare: instead. This is important, because whether á appears just after a or after z as a separate letter depends on the language. localizedCompare: takes care of that.
--edit
As Justin says, localizedStandardCompare: is the selector to be used! I didn't know that method. As written in the documentation, localizedStandardCompare: does more than localizedCompare:, although the document doesn't say exactly what it does.
--end of edit
If you want more, you need to implement that yourself. You can use category for that purpose. First declare it
#interface NSString (MichaelsSuperCompareCategory)
-(NSComparisonResult)michaelsSuperCompare:(NSString*)string;
#end
and then implement it
#interface NSString (MichaelsSuperCompareCategory)
-(NSComparisonResult)michaelsSuperCompare:(NSString*)string{
...
}
#end
This way you can add methods to an existing class. Then you can use
NSArray *array = [[string componentsSeparatedByString:#"\n"]
sortedArrayUsingSelector:#selector(michaelsSuperCompare:)];
It is important to prefix the method name with something distinctive, not to accidentally crash with internal methods used by Apple.
As for the functionality, you need to implement that yourself, as far as I know. You can get the current locale with [NSLocale currentLocale]. You can implement a nicer behavior for the languages you know, and then default to localizedCompare: for unknown languages.
I would somehow do -replaceOccurancesOfStrings on all the data eg "To" -> "" - and then reload the data. (or this can in a text editor)
Another thing to think about is having eg 'to walk' changed to 'walk (to)' which can be done ahead of time (and will also create less confusion for the user as they are scrolling alphabetically).