UISearchBars. Copy Array always equals to Nil - ios

- (void)searchTableList {
NSString *searchString = searchBar.text;
NSString *str=[[stories valueForKeyPath:#"name"] componentsJoinedByString:#"#"];
NSLog(#"desired string:%#",str);
NSMutableArray *array = [[NSMutableArray alloc]init];
array = [str componentsSeparatedByString:#"#"];
//Never attempt to use compare with an array of dictionaries have to extract strings first first
for (NSString *tempStr in stories) {
NSComparisonResult result = [tempStr compare:searchString options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchString length])];
if (result == NSOrderedSame) {
[filteredContentList addObject:tempStr];
}
}
}

First of all, filteredContentList is never allocated in your code above, so it will always point to nil. Add smth like filteredContentList = [NSMutableArray array] in viewDidLoad.
Secondary, you rely on isSearching boolean flag to detect wether your are dealing with search results table view or general table view of you controller. This is, IMHO, bad practice.
You should rely on tableView parameter, which is being passed to every method of table views delegates (in your case -- your UITableViewController). Set tags or compare tableView parameter to self.tableView.
Last thing -- you do not need to call reloadData at viewDidLoad.

Related

iOS: Is there a way to check if an NSArray object contains a certain character?

I need a way to know if an array has the character "#" in one of its string objects. The following code obviously doesn't work because it checks if an object just has the # sign instead of checking if an object contains the # sign. For example, if the user has test#test.com my if statement won't detect it. I need to see if a user has an email or not. I tried researching on how to accomplish this on stackoverflow, but no luck. Any tips or suggestions will be appreciated.
if([answer containsObject:#"#"]){
/// do function.
}
You can check if an NSArray contains an object with containsObject. If it's an array of characters represented as one-character strings, then the code is simple:
NSArray *array = #[#"a", #"b", #"c", #"d"];
BOOL contains = [array containsObject:#"c"];
There's no such thing as an NSArray of scalar types like 'c' char, since the NS collections contain only objects. The nearest thing to an array of chars is an NSString, which has a variety of ways to tell you if a character is present. The simplest looks like this:
NSString *string = #"test#test.com";
NSRange range = [string rangeOfString:#"c"];
BOOL contains = range.location != NSNotFound;
You have to cycle through each NSString in the array and check if it contains the substring.
This custom method shows how:
//assumes all objects in the array are NSStrings
- (BOOL)array:(NSArray *)array containsSubstring:(NSString *)substring {
BOOL containsSubstring = NO;
for (NSString *string in array) {
if ([string rangeOfString:substring].location != NSNotFound) {
containsSubstring = YES;
break;
}
}
return containsSubstring;
}
Usage:
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:3];
[array addObject:#"hi"];
[array addObject:#"yo"];
[array addObject:#"test#test.com"];
BOOL containsSubstring = [self array:array containsSubstring:#"#"];
You could create a custom Category class of NSArray and add the following method:
- (BOOL) containsCharacter:(NSString *) character
{
BOOL characterFound = NO;
for (id object in self)
{
if ([object isKindOfClass:[NSString class]])
{
NSRange range = [object rangeOfString:character];
if (range.location != NSNotFound)
{
characterFound = YES;
break;
}
}
}
return characterFound;
}
Thanks,
Michael
I think that is better way - to use predicates for filtering your array as Larme said.
Try something like this:
NSArray *answer = #[#"John Appleseed", #"john#apple.com", #"john#icloud.com", #"+14120123456", #"invalid#email", #"another###invalid.email"];
NSArray *filteredArray = [answer filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF CONTAINS \"#\""]];
if (filteredArray.count > 0) {
// Do something
}
The filteredArray will contains all objects, which contains at-symbol:
john#apple.com,
john#icloud.com,
invalid#email,
another###invalid.email
Another way - is to filter array by valid email strings and not only at-symbol:
NSArray *answer = #[#"John Appleseed", #"john#apple.com", #"john#icloud.com", #"+14120123456", #"invalid#email", #"another###invalid.email"];
NSString *emailRegex = #"[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSArray *filteredArray = [answer filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF MATCHES %#", emailRegex]];
if (filteredArray.count > 0) {
// Do something
}
The filteredArray will contains only objects, which matches to email-mask
john#apple.com,
john#icloud.com

NSDictionary Searching Issue

I have a plist (an array of dictionary's) that I am reading into an NSArray which I am using to populate a table. It's a list of people and their work location, phone number, etc. I added a UISearchBar and implemented the textDidChange method as well.
When I search by the person's last name I do see the filtered list in the table, however I don't think that I am storing the filtered results properly. I am using an NSMutable Array but I am losing the key:value pairing.
Can someone please point me in the right direction? I ultimately would like to click on a filtered name and push to a detailed view controller. I believe my issue is that I am trying to capture the filtered results in an NSMutableArray but I am not certain.
I've done a lot of Googling but can't seem to put this together in my head. Any help is appreciated.
Kind Regards,
Darin
Here is the array that I am using to load the plist.
-(NSArray *)content
{
if (!content){ {
content = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"ophonebook" ofType:#"plist"]];
NSSortDescriptor* nameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Last" ascending:YES];
content= [content sortedArrayUsingDescriptors:[NSArray arrayWithObject:nameSortDescriptor]];
}
return content;
}
Here is the UISearchBar Method
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (searchText.length == 0)
{
isFiltered= NO;
} else {
isFiltered= YES;
filteredPeople = [[NSMutableArray alloc]init];
for (NSDictionary *dict in content)
{
NSString *last = [dict objectForKey:#"Last"];
NSRange lastRange = [last rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (lastRange.location != NSNotFound)
{
[filteredPeople addObject: [dict objectForKey:#"Last"]];
}
}
}
[myTableView reloadData];
}
Keep things simple, get rid of the flags and the different arrays in your table delegate methods. Instead, have 1 array for your source data (content) and another array for your source data that is actually for display (call it displayContent).
Now, when you start up, set displayContent = content.
Then, when you want to filter, set displayContent = [content filteredArrayUsingPredicate:...]; (you can convert your current loop into a simple predicate).
Finally, when you're done searching, set displayContent = content.
No flags. No ifs in the table delegate methods. Simple, readable code.
p.s. your problem is:
[filteredPeople addObject: [dict objectForKey:#"Last"]];
which you should be setting to:
[filteredPeople addObject:dict];
so you have all the data instead of just the names. Though technically you could still make it work by searching for the last name in your content.

How to test if there is an _NSCFConstantString in an NSMutableArray?

Alright, so I have a Popover, containing a tableView, that is populated by an NSMutableArray filled with strings. But there is always one blank/empty string in my NSMutableArray and in turn always an empty cell in my popover table. I've single stepped my project and found that the empty string is a string constant(_NSCFConstantString).
I've tried to get rid of the empty string occurrence by doing a simple empty string test:
[str isEqualToString:#""]
But this doesn't work, I'm assuming because the empty string in my array is of type _NSCFConstantString...?
So what I'm wondering is if there is a way to test if an object is of type _NSCFConstantString, or if you guys have a better way to test if a string is empty...
Here is my full code that pertains to my issue:
NSString *str;
for (int i = 0; i < [self.flattenedDocList count]; i++) {
str = [self.flattenedDocList objectAtIndex:i];
if(![str isKindOfClass:[NSString class]]){
[self.flattenedDocList removeObject: str];
NSLog(#"Just Deleted:%#",str);
}else if([str isEqualToString:#""]){
[self.flattenedDocList removeObject: str];
NSLog(#"Just Deleted:%#",str);
}
}
The first if-statement is a check to get rid of any NSNull objects in my array. Unfortunately this doesn't get rid of the string constants :/
Thank you, any help is greatly appreciated.
Alright, so I made the rookie mistake of modifying an NSMutableArray while enumerating. Also, H2CO3 was right, _NSCFConstantString IS a concrete subclass of NSString, so we can use all NSString methods on them.
Here is a good way to modify an NSMutableArray while enumerating it.
NSMutableArray *tempArray = [[NSMutableArray alloc] initWithArray:self.docList];
self.listForThePopover = [[NSMutableArray alloc] init];
NSString *str;
for (int i = 0; i < [tempArray count]; i++) {
str = [tempArray objectAtIndex:i];
//NSLog(#"~str:%#~",str);
//check if the str is of the NSString class AND if it's NOT empty
if(([str isKindOfClass:[NSString class]]) && (![str isEqualToString:#""])){
//add the string to the list that we want to actually use.
[self.listForThePopover addObject:str];
//NSLog(#"Just Added:%#",str);
}
}
But this does not work, I'm assuming because the empty string in my array is of type _NSCFConstantString...?
Not quite. _NSCFConstantString is a concrete subclass of NSString, so it should work.
Maybe the string is not really and empty string, or it isn't in the array. Check if it's a space (or more of them), by accident. Examine its length property, etc.
By the way, it's a very bad idea to modify a mutable collection while enumerating it, it can lead to logic errors. Maybe that's also part of your current problem in this case.

Understanding how to use UISearchBar with Core Data

I have an iPad app (Xcode 4.6, iOS 6.2, ARC and Storyboards). I have a UITableView that contains prototype cells, with two labels (lName and lPhone). I have filled a NSArray with the results of the Core Data store. I copied the code from a sample, and am lost! I have two fields I am looking for: name and phone number. I want to be able to search on either one. I tried using the UISearchBar Controller, but the results span the entire window, which is not acceptable. So, I'm trying to do this without the controller. I want the search to filter the shown entries in the UITableView, which this bit of code is supposed to do.
When I do the MR_findAll (MagicalRecord), I get all of the attributes in the Core Data store. This is where I'm lost - how do I get the two attributes out of the array and into the NSMutableArray allTableData, or is it even necessary in this case?
This is my code, so far:
NSArray *allDataArray = [ClientInfo MR_findAll];
// move objects from Core Data store to NSMutablearray
[allTableData addObjectsFromArray:allDataArray];
if(text.length == 0) {
isFiltered = FALSE;
}
else {
isFiltered = true;
filteredTableData = [[NSMutableArray alloc] init];
for (ClientCell* client in allTableData) {
NSRange nameRange = [client.lName.text rangeOfString:text options:NSCaseInsensitiveSearch];
NSRange phoneRange = [client.lPhone.text rangeOfString:text options:NSCaseInsensitiveSearch];
if(nameRange.location != NSNotFound || phoneRange.location != NSNotFound) {
[filteredTableData addObject:client];
}
}
}
I also don't understand how the NSRange is going to match against the two fields I'm looking for. I'm really confused here.
The rangeOfString method returns an NSRange with a location that's not equal to NSNotFound when a given substring is found in the receiver string. What your code does is that it first checks the range of the search text in client.lName.text and client.lPhone.text. Next, the code adds the object to filteredTableData if either of the ranges exist.
As for adding only your two attributes to the filteredTableData, this is simply not needed, as you should access the already stored object to fetch these attributes.
Finally, I'd also recommend you have a look at the free Sensible TableView framework as it should help you perform these kind of searches automatically.
You have to Have to do some thing like this
Fetch the Data from coredata into an array which is pretty mandatory and you have lot of tutorials on that.
And then in the search bar delegate method do implement some thing like this. Which will start filtering your array so that you can see your desired results
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
self.tableView.allowsSelection = YES;
self.tableView.scrollEnabled = YES;
NSArray *list = [[NSArray alloc] initWithArray:artists];
if (searchBar.text && [searchBar.text length] > 0)
{
NSMutableArray *filterContacts = [[NSMutableArray alloc]initWithArray:list];
// NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title LIKE %#",searchBar.text ];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title CONTAINS[c] %#",searchBar.text ];
[filterContacts filterUsingPredicate:predicate];
artists = filterContacts;
}
[self.tableView reloadData];
[self updateSearchString:searchBar.text];
}

NSString from NSArray

I am trying to create a String from Array.But, there is condition to how it should be generated, as explained below.
NSArray *array=[NSArray arrayWithObjects:#"Hello",#"World",nil];
[array componentsJoinedByString:#","];
This will output: Hello,World.
But, if first Item is Empty,then is there way to receive the only second one.
Hello , #"" => Hello
#"" , World => World
Hello , World => Hello,World
Another way to do this is to grab a mutable copy of the array and just remove non valid objects. Something like this perhaps:
NSMutableArray *array = [[NSArray arrayWithObjects:#"",#"World",nil] mutableCopy];
[array removeObject:#""]; // Remove empty strings
[array removeObject:[NSNull null]]; // Or nulls maybe
NSLog(#"%#", [array componentsJoinedByString:#","]);
You cannot store nil values in NSArray*, so the answer is "no". You need to iterate the array yourself, keeping track of whether you need to add a comma or not.
NSMutableString *res = [NSMutableString string];
BOOL first = YES;
for(id item in array) {
if (id == [NSNull null]) continue;
// You can optionally check for item to be an empty string here
if (!first) {
[res appendString:#", "];
} else {
first = NO;
}
[res appendFormat:#"%#", item];
}
* nil values in NS collections are represented with NSNull objects.

Resources