I’d like to add search functionality to a TableView in my app. I populate a table with an NSArray which has x amount of Objects that contain 3 NSStrings. Here’s how I construct that NSArray:
First I create a class Code.h:
#import <Foundation/Foundation.h>
@interface Code : NSObject
@property (nonatomic, strong) NSString *codeName;
@property (nonatomic, strong) NSString *codeNumber;
@property (nonatomic, strong) NSString *codeDesc;
@end
Next, I synthesize these NSStrings in Code.m.
Now in my SearchViewController.m, Here’s how I create my dataset:
NSMutableArray *codes;
codes = [[NSMutableArray alloc] init];
Code *c = [[Code alloc] init];
[c setCodeNumber:@"1"];
[c setCodeName:@"First Title Here"];
[c setCodeDesc:@"I might write a desc in here."];
[codes addObject:c];
c = [[Code alloc] init];
[c setCodeNumber:@"2"];
[c setCodeName:@"Second Title Here"];
[c setCodeDesc:@"2nd desc would be written here."];
[codes addObject:c];
and so on…
Here is how I display it: cellForRowAtIndexPath:
Code *c = [codes objectAtIndex:indexPath.row];
NSString *fused = [NSString stringWithFormat:@"%@ - %@",[c codeNumber],[c codeName]];
cell.textLabel.text = fused;
return cell;
So now that you know how my data is structured and displayed, do you have an idea of how to search either the NSArray or possibly (preferably) the TableCells that have already been created?
I have been through the few tutorials online regarding Adding a Search Bar to a TableView, but all of them are written for using arrays setup using simple arrayWithObjects.
SIDETHOUGHT: Is it possible for me to construct an arrayWithObjects:@"aaa-1",@"bbb-2",@"ccc-3"... from my data? If i can manage that, I can use those tutorials to populate my cells and search them!
UPDATE:
Your second answer makes plenty more sense to me! Thanks for that. I beleive I have followed your instruction, but I am getting a “-[Code search:]: unrecognized selector sent to instance 0x6a2eb20` when that line is hit.
- I added
@property (nonatomic, strong) NSString *searchString;toCode.hand synthesized it inCode.m - I added
NSMutableSet *searchResults;toSearchViewController.h‘s@interface - I added your methods
performSearchWithStringandmatchFoundtoSearchViewController.m - Directly under those I added this to call
performSearchWithString
x
- (void)searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchString {
NSLog(@"%@",searchString); //Just making sure searchString is set
[self performSearchWithString:searchString];
[self.tableView reloadData];
}
The error hits when [codes makeObjectsPerformSelector:@selector(search:) withObject:self]; runs. I am confused b/c it sounds like Code doesn’t recognize searchString, but I know I added it in Code.h.
UPDATE:
In order to store objects in searchResults, I had to change searchResults from a NSMutableSet to a NSMutableArray and modify - (void)matchFound:(Code *) matchingCode {} to this:
-(void) matchFound:(Code *) matchingCode {
Code *match = [[Code alloc] init];
if (searchResults.count == 0) {
searchResults = [[NSMutableArray alloc] init];
[match setCodeName:[matchingCode codeName]];
[match setCodeNumber:[matchingCode codeNumber]];
[match setCodeDesc:[matchingCode codeDesc]];
[searchResults addObject:match];
}
else
{
match = [[Code alloc] init];
[match setCodeName:[matchingCode codeName]];
[match setCodeNumber:[matchingCode codeNumber]];
[match setCodeDesc:[matchingCode codeDesc]];
[searchResults addObject:match];
}
With a few other tweeks, I’ve got a working searchbar for my tableView. Thanks Tim Kemp!
Oh, also case insensitive search was what I was looking for. NSRange rangeName = [codeName rangeOfString: searchString options:NSCaseInsensitiveSearch];
I hope this question and answer will be helpful to the next developer learning objective-c with this question!
Simpler approach
You asked for a simpler solution. This one isn’t nearly as flexible, but it will achieve the same things as my earlier answer for this specific case.
Once again we are going to ask
Codeto search its strings for us. This time, we are going to skip theSearchRequestand the block callback and implement it directly.In your
SearchViewControlleryou will create two methods. One to do the search, and one callback to process any results as they come back. You will also need a container to store matchingCodeobjects (more than one might match, presumably.) You will also need to add a method toCodeto tell it what the search string is.NSMutableSetcalledsearchResultstoSearchViewController.NSString *calledsearchStringtoCodeAdd the search method to
SearchViewController. This is what you’ll call when you want to initiate a search across all your codes:Then you will also need a callback in
SearchViewController. This is so that yourCodeobjects can tell theSearchViewControllerthat they have found a match:However do note that you don’t have to use the
searchResultsmutable set; you may well want to just call another method to immediately add the returned result to some other list on screen. It depends on your app’s needs.In
Code, add a search method a bit like we had before, but instead of theSearchRequestparameter we’ll pass in a reference to theSearchViewController:Do you see how that works? If there’s a match in any of the strings (
||means ‘or’) then passself(which means exactly what it sounds like: the current object that’s running this code right now) back to a method in the view controller calledsearchVC. This is called a callback because we are “calling back” to the object which originally sent us the message to do the search. We have to use callbacks rather than simple return types because we have usedmakeObjectsPerformSelectorto tell every singleCodein thecodesarray to do a search. We never explicitly called thesearchmethod ourselves, so we have no way to capture the return value from eachsearch. That’s why its return type isvoid.You can extend
matchFoundto take an additional parameter which identifies which string the match was in (i.e.çodeNumber,codeNameorcodeDesc.) Look intoenumsas one good approach to pass around that kind of data.Hope that’s bit simpler.
Here is a link to an excellent language introduction/tutorial which will eliminate much confusion.
EDIT In your last comment you said that
searchResultswas null. I said to add it as an ivar somewhere inSearchViewController. In your initialiser method for SearchViewController you should callAlternatively you could ‘lazy initialise’ it in
matchFound:Though if you do this you should be aware that anywhere else you access
searchResultsmay find that it’s null ifmatchCode:has never previously been called.