UISearchBar that Ignores Special Characters when Searching UITableView Cell - ios

Thanks to the help of those on SO, I have a great UISearchBar that filters my UITableView. There is one more feature that I'd like to add.
I would like the UISearchBar filter to ignore special characters like apostrophes, commas, dashes, etc... and to allow cells with text like "Jim's Event" or "Jims-Event" to still come up if the user types "Jims Event".
for (NSDictionary *item in listItems)
{
if ([scope isEqualToString:#"All"] || [[item objectForKey:#"type"]
isEqualToString:scope] || scope == nil)
{
NSStringCompareOptions opts = (NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch);
NSRange resultRange = [[item objectForKey:#"name"] rangeOfString:searchText
options:opts];
if (resultRange.location != NSNotFound) {
[filteredListItems addObject:item];
}
}
}
Anyone have any ideas? Thank you!

This one is a little tricky. The first solution that comes to mind is to strip any character that you deliberately don't want to match from both the search and item strings, then do the comparison. You can use NSCharacterSet instances to do that filtering:
// Use this method to filter all instances of unwanted characters from `str`
- (NSString *)string:(NSString *)str filteringCharactersInSet:(NSCharacterSet *)set {
return [[str componentsSeparatedByCharactersInSet:set]
componentsJoinedByString:#""];
}
// Then, in your search function....
NSCharacterSet *unwantedCharacters = [[NSCharacterSet alphanumericCharacterSet]
invertedSet];
NSString *strippedItemName = [self string:[item objectForKey:#"name"]
filteringCharactersInSet:unwantedCharacters];
NSString *strippedSearch = [self string:searchText
filteringCharactersInSet:unwantedCharacters];
Once you have the stripped strings, you can do your search, using strippedItemName in place of [item objectForKey:#"name"] and strippedSearch in place of searchText.
In your example, this would:
Translate the search string "Jims Event" to "JimsEvent" (stripping the space)
Translate an item "Jim's Event" to "JimsEvent" (stripping the apostrophe and space)
Match the two, since they're the same string
You might consider stripping your search text of unwanted characters once, before you loop over item names, rather than redoing the same work every iteration of your loop. You can also filter more or fewer characters by using a set other than alphanumericCharacterSet - take a look at the class reference for more.
Edit: we need to use a custom function to get rid of all characters in the given set. Just using -[NSString stringByTrimmingCharactersInSet:] only filters from the ends of the string, not anywhere in the string. We get around that by splitting the original string on the unwanted characters (dropping them in the process), then rejoining the components with an empty string.

Related

Is that integer equal to the number in textfield

I'm new to Xcode and Objective-C. I watched a tutorial where he compared a char with the entered text in the textfield of the simulator. He used the following code:
BOOL isUsersEqual = [self.username isEqualToString:[self.usernameTextField text]];
When trying to build a small app myself I have problems to get the number out of the Textfield and be processed into my code. I want it to compare the random number I generated to the number in the textfield. How can I accomplish that?
I know this is kind of a nooby question and I don't wanna ask every time. Is there someplace I can look up all those types of questions?
You can try it by converting the number into string and then comparing -
NSString *number = [self.username stringValue];
if(number isEqualToString:self.usernameTextField.text){
//Do Something
}
else{
//Do Something
}
But this method only works when you're comparing Equals To -
For Greater than/ Less than/Equal to you can try it by converting the textfield's text into number and then comparing -
NSNumber *number = [self.usernameTextField.text integerValue];
if(number == self.username){
//Do Something
}
else{
//Do Something
}
To compare your generated number with a number from textField, you must first typecast the textField text to NSInteger using
NSInteger b = [[textField text] integerValue];
then you can proceed to compare the 2 numbers.
To fully understand these types of questions, I recommend you learning about objective-c primitive types and typecasting. Good luck!

NSMutableAttributedString will not display attribute that begins at nonzero index

UPDATE:
I created a really simple standalone project to demonstrate the bug. If anyone would like to pull same and see if they can spot where I've gone wrong, I'd sure appreciate it. There's not much code to look through. Public repo here:
https://github.com/reidnez/NSAttributedStringBugDemo
I'm having a very strange issue here: I have a tableview. Each cell has a title label with 1-3 words, and a keywords label with several CSV keywords. I also have a search bar. The requirement is that as the user types into the search bar, any partial matches on both the title and keywords for each cell are shown highlighted. Screenshots:
First image is A-Okay. In the second image, the "an" of the title label should be highlighted. But, as you can see, not so much...
This works perfectly fine on the "keywords" label, as you can see above. The attributed strings for both of these labels are created by a category I wrote (code below). The same method is called on both strings, and appears to behave the same from what the debugger is telling me. The UI tells a different story.
I have stepped through the debugger numerous times, and in all cases, the attributed string appears to have been configured correctly. I have also verified that something else is not calling [tableView reloadData] and that no other place in my code is overwriting the label's value. This is how matching on "an" for "Fang" looks in the debugger, just before the cell is returned at the end of cellForRowAtIndexPath:
(lldb) po customCell.entryTitleLabel.attributedText
F{
}an{
NSBackgroundColor = "UIDeviceRGBColorSpace 0.533333 0.835294 0.156863 1";
}g{
}
Looks good to me...that is exactly what I want. But when the cell renders, there are NO highlights to be seen! Weirder yet, as an experiment I tried setting the label to a completely arbitrary attributedString that I created right in cellForRow:
NSMutableAttributedString *fake = [[NSMutableAttributedString alloc] initWithString:#"Fang"];
[fake addAttribute:NSBackgroundColorAttributeName value:MATCH_TEXT_HILIGHT_COLOR range:NSMakeRange(1, 2)];
customCell.entryTitleLabel.attributedText = fake;
This, too fails. No highlighting at all...but I CAN highlight any substring in the range of {0, 1} to {0, fake.length} and it behaves as expected. Again, it seemingly refuses to highlight any substring that does not begin at index 0--but only for the title label.
Am I losing my mind? What am I missing?
Below is my category...but I am fairly confident the problem does not lie here, because it functions perfectly for the keywords string, and (again) the attributes appear to be set correctly just before the cell returns:
-(void)hilightMatchingSubstring:(NSString*)substring color:(UIColor*)hilightColor range:(NSRange)range
{
if ([self.string compare:substring options:NSCaseInsensitiveSearch] == NSOrderedSame) {
[self addAttribute:NSBackgroundColorAttributeName value:hilightColor range:NSMakeRange(0, self.length)];
return;
}
// Sanity check. Make sure a valid range has been passed so that we don't get out-of-bounds crashes. Default to return self wrapped in an attributed string with no attributes.
NSRange selfRange = NSMakeRange(0, self.length);
if (NSIntersectionRange(selfRange, range).length == 0) {
NSLog(#" \n\n\n*** Match range {%lu, %lu} does not intersect main string's range {%lu, %lu}. Aborting *** \n\n\n", (unsigned long)range.location, (unsigned long)range.length, (unsigned long)selfRange.location, (unsigned long)selfRange.length);
return;
}
if (substring.length > 0) {
NSRange movingRange = NSMakeRange(range.location, substring.length);
if (NSMaxRange(movingRange) > self.length) {
return;
}
NSString *movingString = [self.string substringWithRange:movingRange];
while (NSMaxRange(movingRange) < NSMaxRange(range)) {
if ([movingString compare:substring options:NSCaseInsensitiveSearch] == NSOrderedSame) {
[self addAttribute:NSBackgroundColorAttributeName value:hilightColor range:movingRange];
}
movingRange = NSMakeRange(movingRange.location + 1, substring.length);
movingString = [self.string substringWithRange:movingRange];
}
} // This is fine...string leaves properly attributed.
}
Thanks for writing this up... Thought I was going crazy too!
I came up with a workaround (read: hack) whilst we wait for something official from Apple.
NSDictionary *hackAttribute = [NSDictionary dictionaryWithObjectsAndKeys:
[UIColor clearColor], NSBackgroundColorAttributeName, nil];
NSMutableAttributedString *attributedText =
[[NSMutableAttributedString alloc] initWithString:#"some text..."];
[attributedAddressText setAttributes:hackAttribute range:NSMakeRange(0, attributedText.length)];
// Then set your other attributes as per normal
Hope that helps.

iOS6 failed to check string contains only alpha-numeric chars

I am using this code to check if a user input string contains only alpha-numeric chars in iOS6:
NSCharacterSet *alphaSet = [NSCharacterSet alphanumericCharacterSet];
NSString *trimmedString = [text stringByTrimmingCharactersInSet:alphaSet];
BOOL valid = [trimmedString isEqualToString:#""];
but here's the result of running this piece of code:
Note that the text (string to be checked) contains only Chinese chars, but after trim by alphaSet it becomes an empty string and passed the verification.
I also tried this code and it is also not working:
- (BOOL) isAlphaNumeric: (NSString *) text
{
NSCharacterSet *unwantedCharacters =
[[NSCharacterSet alphanumericCharacterSet] invertedSet];
return ([text rangeOfCharacterFromSet:unwantedCharacters].location == NSNotFound) ? YES : NO;
}
I am not sure where I am doing wrong. Do I need to encode the input text somehow before I do the check?
Currently I get the string directly from iOS6's - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text's last replacementText parameter.
Thanks!
The problem is that alphanumericCharacterSet also seems to contain the chinese letters, so the result is "correct". Apple doesn't document it this way but I've seen a few postings (e.g. from GNUStep) that seem to indicate it. (Also note that Apple does document that this set contains characters that you might not want: Informally, this set is the set of all characters used as basic units of alphabets, syllabaries, ideographs, and digits.).
A simple yet inelegant workaround is to define the character set yourself, if all you want is the english alphabet without any foreign letters like Ü or ß:
NSCharacterSet *alphaSet = [NSCharacterSet characterSetWithCharactersInString:#"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890"];
When you're targeting iOS >= 7, one of the URL*AllowedCharacterSet might be of interest. See the NSCharacterSet documentation for details.

iOS: Column formatting for NSString not working in UITextView?

I'm working on an iPad app and I need to be able to format some output on the screen in a columnar format. This was similar to my question:
How can I use the \t [tab] operator to format a NSString in columns?
and I used that solution, unfortunately I'm not seeing the same results. My code is as follows:
NSString* titleColumn = [[NSString stringWithFormat:#"%#", displayName] stringByPaddingToLength:25 withString:#" " startingAtIndex:0];
NSString* serializedValue = [[NSMutableString alloc] init];
NSString* valueAsString = [NSString stringWithFormat:#"%.03f", value];
serializedValue = [serializedValue stringByAppendingFormat:#"%#%#", titleColumn, valueAsString];
When logging the items in the console, they are properly aligned, however, when plugging them into a UITextView, they are misaligned. This is how I'm sticking them in the text view:
NSMutableString* displayString = [[NSMutableString alloc] initWithString:#""];
for (NSString* entry in textToDisplay)
{
NSLog(#"%#", entry);
[displayString appendString:entry];
[displayString appendString:#"\n"];
}
[self.fTextPanel setText:displayString];
Any ideas on what I'm doing wrong?
EDIT:
In the log, it looks like this:
Inclination 0.000
Version 0.000
Inferior/Superior 0.000
Anterior/Posterior 0.500
And the UITextView version looks like this: http://imgur.com/vrUzybP,OYxGVd8
The reason for misaligned is the different width for each character. The best way for displaying this type of information is by using UITableView, You can use title and subTitle field for displaying or you can design a custom UITableView.
the reason is because when drawing charachters (glymphs), each character will have different widths,i.e character small c and big C will take different widths for drawing , so it wont work. Even though there are two strings with equal lengths but different characters, the drawing text will have different widths.
What i would suggest you for this is to have two textViews side by side, render titles in one textVIew and values in the next TextView.
If the number of titles is large and if you enable scrolling in textVIews, use delegate Methods of UIScrollView, and when scrollHappens in either of them, set the contentOffset of the other to make it look like single TextView.
Hope this helps.

Replace lines of text in iOS

I am needing to replace a line of text in code on my iOS app, however the lines that need to be replaced in the particular NSString will be different for different entries on the XML that is being parsed.
For example, I need to replace 129727-the-cave.mp3 with 129727.jpg.
However, I can't just tell it to replace -the-cave.mp3, because some instances of the string will have a different number and title of mp3. I think the next line is:
129838-my-song.mp3.
So, basically, I need a way to find everything from the first hyphen through mp3 and replace it, no matter what the text is?
Try this:
NSString *original = #"129727-the-cave.mp3";
NSString *sub = [original substringToIndex:[original rangeOfString:#"-"].location];
NSString *finalString = [sub stringByAppendingString:#".jpg"];
NSLog(#"finalString: %#", finalString);

Resources