For a project I am working on, I needed to implement a search bar that would automatically complete the word the user is typing into the search bar and display relevant results. However, I didn’t want this to work like the default search bar functionality in objective c where the search bar is tied to a table view that takes up most of the on screen real estate. Instead I needed this auto complete search bar to visually be more like the search bar on Google. I have attached an example Xcode Project that demonstrates this functionality on an iPhone.
As can be seen in the image above the only two objects needed to make this work are a UITextField and a UITableView. The UITextField will act as our search bar, and the UITableView will be populated with our auto complete elements. Other than that you just need to have the correct logic.
Step 1: Create Objects
First, we will be creating the UITextField and the UITableView. The table and text fields can be configured according to what is needed in your app, but I would always create the UITable with a width of about 2 to 4 pixels less than the text field. This will allow the auto complete functionality to be visually more like the Google search bar. The one thing to note is that the table is initially hidden. This is done because we only want the table to display when the user is typing into the text field.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//Search Bar
txtField = [[UITextField alloc] initWithFrame:CGRectMake(5, 20, 261, 41)];
txtField.borderStyle = 3;
// rounded, recessed rectangle
txtField.autocorrectionType = UITextAutocorrectionTypeNo;
txtField.textAlignment = UITextAlignmentLeft;
txtField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
txtField.returnKeyType = UIReturnKeyDone;
txtField.font = [UIFont fontWithName:@
"Trebuchet MS"
size:22];
txtField.textColor = [UIColor blackColor];
[txtField setDelegate:self];
[self.view addSubview:txtField];
[txtField release];
//Autocomplete Table
autoCompleteTableView = [[UITableView alloc] initWithFrame:CGRectMake(6, 56, 259, tableHeight) style:UITableViewStylePlain];
autoCompleteTableView.delegate = self;
autoCompleteTableView.dataSource = self;
autoCompleteTableView.scrollEnabled = YES;
autoCompleteTableView.hidden = YES;
autoCompleteTableView.rowHeight = tableHeight;
[self.view addSubview:autoCompleteTableView];
[autoCompleteTableView release];
|
We now need to create an array that will be what we are searching through to populate the autoCompleteTableView. I have included a text file with sample elements to populate this array. We first need to pull the contents of the file into memory and then convert the bytes from the file into a string. This string will then be split around newline characters to create our array. Finally, we will be creating our auto complete array that will hold the elements that correspond with what is being typed in the search bar.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//Pull the content from the file into memory
NSString *filePath = [NSString stringWithFormat:@
"/Users/drewnextpression/AutoCompleteTextField/elements.txt"
];
NSData* data = [NSData dataWithContentsOfFile:filePath];
//Convert the bytes from the file into a string
NSString* string = [[[NSString alloc] initWithBytes:[data bytes]
length:[data length]
encoding:NSUTF8StringEncoding] autorelease];
//Split the string around newline characters to create an array
NSString* delimiter = @
"\n"
;
NSArray *item = [string componentsSeparatedByString:delimiter];
elementArray = [[NSMutableArray alloc] initWithArray:item];
autoCompleteArray = [[NSMutableArray alloc] init];
|
Step 2: Text Field Logic
As the user types into the text field, the text will be converted into a substring by using UITextfield’s delegate method shouldChangeCharactersInRange. Once this substring has been created, it will be passed to the method searchAutocompleteEntriesWithSubstring.
The method searchAutocompleteEntriesWithSubstring will compare the substring of the text field against the elements in the element array to see if any of the element’s beginning characters are the same as the substring. This is done by grabbing each element one at a time from the array of elements. An NSRange value is then created that will have the range and location of the substring in the element. Since I wanted the search to be case insensitive I have two different ranges. One NSRange will check for lowercase and another will check for uppercase.
Once these ranges are found, their length can be used to determine if the substring occurred at the beginning of the element. Since NSRange will return a length of 0 if the substring isn’t in the beginning of the current element, we can use this logic to only add elements to the autoCompleteArray that have an NSRange of length != 0.
Finally, the table needs to be unhidden since the user is typing into the text field, and it needs to be reloaded since it is being drawn based on the elements in the autoCompleteArray.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// String in Search textfield
- (
BOOL
)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *substring = [NSString stringWithString:textField.text];
substring = [substring stringByReplacingCharactersInRange:range withString:string];
[self searchAutocompleteEntriesWithSubstring:substring];
return
YES;
}
// Take string from Search Textfield and compare it with autocomplete array
- (
void
)searchAutocompleteEntriesWithSubstring:(NSString *)substring {
// Put anything that starts with this substring into the autoCompleteArray
// The items in this array is what will show up in the table view
[autoCompleteArray removeAllObjects];
for
(NSString *curString in elementArray) {
NSRange substringRangeLowerCase = [curString rangeOfString:[substring lowercaseString]];
NSRange substringRangeUpperCase = [curString rangeOfString:[substring uppercaseString]];
if
(substringRangeLowerCase.length != 0 || substringRangeUpperCase.length != 0) {
[autoCompleteArray addObject:curString];
}
}
autoCompleteTableView.hidden = NO;
[autoCompleteTableView reloadData];
}
|
Step 3: TableView Logic
As noted in step 2, the table is being drawn based on the elements in the autoCompleteArray. If the array is empty, the table will be drawn with one empty cell. If the array has one element, the table will be drawn with one cell displaying this element. If the array has two elements, the table will be drawn with two cells displaying the elements. If the array has three elements or more, the table will be drawn with three cells and will be scrollable if there are more than three objects. The code for populating the table is found in the table delegate method cellForRowAtIndexPath, and the table drawing functionality is found in the tableview delegate method numberOfRowsInSection.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger) section {
//Resize auto complete table based on how many elements will be displayed in the table
if
(autoCompleteArray.count >=3) {
autoCompleteTableView.frame = CGRectMake(6, 56, 259, tableHeight*3);
return
autoCompleteArray.count;
}
else
if
(autoCompleteArray.count == 2) {
autoCompleteTableView.frame = CGRectMake(6, 56, 259, tableHeight*2);
return
autoCompleteArray.count;
}
else
{
autoCompleteTableView.frame = CGRectMake(6, 56, 259, tableHeight);
return
autoCompleteArray.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
static
NSString *AutoCompleteRowIdentifier = @
"AutoCompleteRowIdentifier"
;
cell = [tableView dequeueReusableCellWithIdentifier:AutoCompleteRowIdentifier];
if
(cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:AutoCompleteRowIdentifier] autorelease];
}
cell.textLabel.text = [autoCompleteArray objectAtIndex:indexPath.row];
return
cell;
}
|
When the user clicks on an element in the table, the textfield will be populated with that element and the method finishedSearching will be called. This method will close the keyboard and hide the table.
1
2
3
4
5
6
7
8
9
10
|
- (
void
)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
txtField.text = selectedCell.textLabel.text;
[self finishedSearching];
}
- (
void
) finishedSearching {
[txtField resignFirstResponder];
autoCompleteTableView.hidden = YES;
}
|
Step 4: Closing the Keyboard
I wanted the user to be able to close the keyboard by either pressing the Done button on the keyboard or tapping anywhere on the background. I used the text field’s delegate method textFieldShouldReturn to close the keyboard on a Done button press. Finally, I used the method touchesBegan: withEvent: to close the keyboard when the user touches the background.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// Close keyboard when Enter or Done is pressed
- (
BOOL
)textFieldShouldReturn:(UITextField *)textField {
BOOL
isDone = YES;
if
(isDone) {
[self finishedSearching];
return
YES;
}
else
{
return
NO;
}
}
// Close keyboard if the Background is touched
- (
void
)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
[super touchesBegan:touches withEvent:event];
[self finishedSearching];
}
|
Step 5: Clean up and Release
At this point we need to release the autoCompleteArray and elementArray. The table and text field don’t need to be released since we already released them after we initialized them.
1
2
3
4
5
|
- (
void
)dealloc {
[autoCompleteArray dealloc];
[elementArray dealloc];
[super dealloc];
}
|
That’s it! Download the AutoCompleteTextField project and give it a go. If you find any bugs or have any suggestions let me know.
Source Code: AutoCompleteTextField.zip