Objective-C iPhone Programming Tutorial - UISearchBar
Hey guys, this is Maads 101 and today I'm going to be showing you how to make a simple table view that you can search using a search bar.
So for instance, in this example, I can just type something and it'll show all the results. I can hit enter to just get rid of the keyboard and what have you. So, uh, this is pretty much what we're going to be making in this tutorial.
The first thing you will do is open Xcode and I will create a file, new project. All right, and a view-based application and I'll call it "Searching". All right, create, and here we are.
Now, the first thing that we're going to do is set up the interface in the way that we had it configured in the app I just showed you. So we're going to click on "Searching ViewController.xib" right here, and then we're going to go to "View Utilities" and I'll show the inspector. I guess it really doesn't matter.
Okay, now throw this over here, find a table view, drag it on there. I'll also find a search bar and I'll drag the search bar up to the top.
Okay, so now we have our interface set up, but we don't have it set up with code. So I'm just going to be creating some of the variables and outlets that we need to use in order to make this interface actually work with our code.
So I'll go to "SearchingViewController.h" and I'll use curly braces right here and I'll declare an IBOutlet UITableView tableView, and IBOutlet, sorry, IBOutlet UISearchView searchBar, and then right up here next to the UIViewController, I'm going to have a less than and greater than, and I'm going to put UITableViewDelegate, UITableViewDataSource, and UISearchBarDelegate separated by commas, by the way.
So my header is basically set up like this and now I can go back into the xib. And by the way, you have to save the header by pressing Command S. Now I'll go back into the xib and link up our code with our interface.
So you want to hold control while dragging from Files Owner over to this table view and then do the same thing over to the search bar and just tap search bar.
Okay, now we’re going to do the same thing, so control-drag from the table view over to Files Owner and select data source. Then do the exact same thing and this time select delegate. Then you're going to want to select the search bar just by clicking on it until it gets highlighted like it is here, and then control-drag over from it to Files Owner and then hit delegate again.
Now we've set up basically our entire interface so that way our code can access the elements in our interface and our interface can access our code, and you'll see how that is important in just a minute.
Okay, so now what we're going to do is write the implementation itself. So just go ahead and click on "SearchingView.m" and actually before we do this, let's go into "SearchingView." and we're going to set up a couple of things.
If you'll recall from the demo I showed you at the beginning of this video, we basically need a list of items. Now, in this video, it’s just going to be the words for all the numbers like 1, 2, 3, 4, 5, 6, 7, etc.
Then we're also going to need a list that's all of these search results that are actually being shown. So I'm going to create an array and that's what a list is in programming called "allItems".
Okay, and this will always contain all of the items in the table view, not just the ones that are being shown. Now we also need an array for the search results or for the items that are being actually displayed, and this can change.
If you'll remember, when I change what I'm searching, this will change as well based on the results. So we're going to need to declare this as an NSMutableArray and I'll call it "displayItems".
Now, NSMutableArray, you'll notice the word mutable here because mutable comes from the word mutate, and that basically means that we can change it. This is always going to be the way it is when we start it off. You know, it can’t be changed. This we can add items, we can remove items, we can filter it based on search results, etc.
So now we're going to go back into our .m and just implement viewDidLoad here, and this is where we're going to be setting up both of these arrays for the first time.
So first of all, I’ll set up allItems, so I’ll say allItems equals NSArray alloc initWithObjects. Okay, and the objects are strings separated by commas, so a string is like an @, then between the two quotes you put whatever you want.
So, I have 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, and 11. Now put a semicolon at the end. Now we're also going to want to create the displayItems, and we’re just going to start it off initially with all the same things as allItems because all the things are going to start off being displayed.
So we’ll say displayItems equals NSMutableArray alloc initWithArray and then a colon and we’ll just give it allItems. This will basically create a mutable, if you will, version of this that can be changed.
So the only difference right now between displayItems and allItems is that displayItems can be modified, and we're going to be doing that once we get into actually implementing the search feature.
But before we do that, we have to make the table view actually display whatever's in displayItems because that's what it's called, displayItems. You know, it's got to display what's in there.
So there are a couple methods we have to implement for the table view data source that are going to allow us to give the table view all of these items.
The first one returns an NSInteger and it's called numberOfSectionsInTableView, and this is always going to return one because there's only one section in our table view. The next one is the same kind of idea, it's how many rows are in a section, and since there's one section, we don't have to worry about which section they're asking for.
We'll just tell it how many rows are in displayItems, so we'll give it displayItems.count and that'll tell it how many items that'll basically display.
Okay, now this last method that we're going to implement will give the table view a cell for whatever row that it wants.
So, let's say that it needs to know what's in row one so it can display the contents of row one. It's going to call one sec. It’s called tableView:cellForRowAtIndexPath. Sorry, so basically in here we’re going to create a cell with the text of the current item.
So, um, in this case, whichever row they're asking for will be one of these things in this array, so 1, 2, 3, 4, whatever in displayItems.
So before we can even worry about what's actually in the row, we have to create the cell. So we’re going to say UITableViewCell *cell equals... and whoops, capitalize the E there equals tableView dequeueReusableCellWithIdentifier and then we'll just give it a nice standard identifier.
Now it's giving us a warning here because look at this, this is a parameter that we're getting, and if you'll recall, we have an instance variable up here with the same name.
So, uh, what we're going to do is just change this to, let’s say, tableView. Whatever, it doesn’t matter. We don’t really care about this because we're only dealing with one table view, so it's kind of irrelevant.
So we can call it whatever we want as long as it doesn’t collide with our instance variable. Okay, now we are going to say if (!cell) and basically what this is doing is we’re asking the table view, and table views do this magic thing where if they don’t need a cell anymore because it’s invisible, you know, it’s been pushed off the screen by scrolling, it saves that cell and it lets you reuse it for a different row later on.
But if it hasn’t saved any cells, then we need to create a new cell, so here’s where we’ll do that.
We’ll say UITableViewCell alloc initWithStyle and we'll give it UITableViewCellStyleDefault, and the reuseIdentifier will have to match the one we use here if we want to actually take advantage of this reusable recycling cell system.
It's just "cell" with the capital C, and that has to be the same. And now we'll actually set the text of the cell to be that of the item that they're asking for, so cell.textLabel.text equals displayItems objectAtIndex indexPath.row. Basically we are just getting whichever item they ask for.
So let’s say they ask for item three, we would just get the third item of displayItems, which happens to be three probably, and we would set the text of the cell to be that.
And now we’re finally going to return the cell, we're returning it to the table view so they can actually use it and look at it. So right now, if we run it, the table view will just show whatever we have in displayItems, which will always be these things.
So let's go ahead and run it, and as you see here, all our items are there that we put in that table in that array, and we can even type something in the search box, but nothing happens, it's not filtered, whatever, because we haven't made anything happen yet.
So now what we have to do is make the search feature actually work, and what this consists of is just simply whenever they type anything into this box, this search box right here, it will change whatever's in displayItems to only have the results of whatever they searched.
So we're going to declare a couple or we're going to implement a couple new methods in our .m for the search field. So the first one, and this is probably the most important one, is searchBar:textDidChange, and we will get this code called whenever the user enters any text into the search bar.
Whether that be, for instance, getting rid of the text, you know, if they hit backspace, this will get called. If they hit a letter, this will get called. If they hit the clear button, this will get called.
So this will get called whenever we need to change our results based on what the user is searching. So basically what we're going to do is we're going to see... and another thing you’ll notice before I even go on is that if the user searches an empty piece of text, like if they have nothing in the search field, it just shows all of the results.
Even though no results would actually come up hypothetically, I guess. So, uh, we're going to need to check if the text is actually... if there’s any text.
So we’ll say if (searchText.length == 0), and basically this code will get called, this code will run right here if there’s nothing in the search field, and otherwise, this code will get called right here if there is something in the search field.
So if there’s nothing in the search field, what we’re going to do is we’re going to say displayItems removeAllObjects and that just removes everything so nothing will be displayed right now.
And then what we want to do so we actually display everything is we say displayItems addObjectsFromArray allItems. So first, it'll get rid of all of the items so that way nothing is displayed, and then it’ll add all of the items so that way everything is displayed.
And the reason we have to do this two-step process is because if we just add all of the items and half of them were already there because there was already a search result, then some items will be there twice. So that’s why we’re going to remove all the things first and then add everything back.
All right, and now if we’re actually searching, we’re doing a filter. What we need to basically do is go through all of the items in allItems, and then if it matches what they're searching, then we’ll add it to displayItems.
So first of all, we’re going to say displayItems removeAllObjects. Okay, and now we’re going to go through all of the things in allItems, so for (NSString *string in allItems), and this code will run right here for every single item.
So for one, for two, for three, for four, for five, you know, everything in allItems, and string which is our magic variable will be different each time. It'll be one in one case and two in one case and three in one case and etc.
And we’re basically going to be checking if string matches the search text, and if it does, then we will add it to displayItems. Simple as that.
So in order to check if searchText, and in this case, we’re just doing a very simple search. We’re just checking if this string, if this piece of text contains or has inside of it the text that they’re searching for.
So we’re going to do NSRange r = [string rangeOfString:searchText options:NSCaseInsensitiveSearch], and basically an NSRange is just a type that tells us where it is.
So if the string, in fact, and the string might be three or two or whatever if it has inside of it searchText. So let’s say they search "W", "2" has a "w" in it and it's the second character, but if they search "W", "4" definitely doesn't have a "w" in it.
Okay, so, um, in this case, we’re going to check if it was actually found in this string, so we’re going to say if (r.location != NSNotFound). Okay, and in this case, if it’s not found, then we don’t want to do anything.
So actually what we’re going to do is we’re going to say if it is found, we’re going to add it to displayItems. So we’re going to say displayItems addObject:string. So for every piece of text, every element in allItems that actually matches the search text, we’re going to add it to displayItems.
And then at the end, if you’ll recall from another tutorial where I had to use table views, what you want to do is reload the data in the table view to match whatever we’ve put in the displayItems because the table view doesn’t just constantly ask for the same items again and again to check if anything’s changed.
We actually have to tell it that a change has been made, so in order to do that, we have to say tableView reloadData. And we're just doing that at the end so no matter what happens, it’ll always reload the data.
Okay, so let's go ahead and run this right now, and let's see how it goes.
All right, so let me just type "O" and you’ll see a couple results come up. I’ll search "W", only "2" is the only result. Search "O" again, and if I hit "X", then everything shows up.
Okay, great, but now let’s say I want to get rid of the keyboard. How do I do that? Even if I type something and I hit search, it doesn’t go away, you know?
So there’s no way to escape this, and also if I scroll, you know I can’t get to the bottom because the keyboard is covering half of the list. So these are two problems that now we have to work on, um, two kind of big problems so let’s go over here back into our implementation.
And we’re going to do something. First of all, if the user hits the search key on the keyboard, we’re going to make it hide the keyboard. That’s what we want to do anyway.
So we’re going to have a new void searchBarSearchButtonClicked. Okay, and like I mentioned before, if we have searchBar right here and we also have searchBar up in our .h, it doesn't know which one we're trying to access.
So I’m just going to rename this to searchBar so that way it has a different name and we didn’t have to do that in here because we're not accessing searchBar in here, so it doesn't matter if there are two.
You know, it really doesn’t matter, but here it does. So we’re going to say searchBar resignFirstResponder and this line of code, this very simple line of code is actually what will hide the keyboard.
So now if we go ahead and run the project again and we go ahead and type something like "O" and we hit enter, Boom! The keyboard is hidden, and now we can see this stuff again.
Whoops, I don't know what just happened there. Okay, well anyway, so that’s that. Now let’s say, let’s face the other issue that we were having where if we tap here and the keyboard comes up, we actually can’t get to the bottom of the list unless we hide the keyboard by hitting enter.
And that’s because the keyboard just comes up and covers our table view. It’s kind of obnoxious that it does that, but you know, we haven't told it any better.
So in order to correct this problem, we're basically going to need to resize the table view to not go down all the way. So that way the keyboard doesn’t cover anything. And then when the keyboard goes away, we have to resize it again to be the full height.
So that way it doesn’t look like the table view is only half the height of the actual program. So let’s do the first thing I mentioned first. That’ll fix half of the problem.
So in our viewDidLoad, we’re going to say NSNotificationCenter defaultCenter addObserver. Okay, self, and then for the selector, we’ll give it atSelector and how about keyboardShown, and the name will be UIKeyboardDidShowNotification and the object will be nil.
Or nil, rather. It doesn’t really matter. Nil is what I like. And now down here, we’re going to implement yet another method, which is keyboardShown. Whoops! And it’ll take an NSNotification *note.
I’ll call it "note". Okay, so this code will get called every single time the keyboard gets displayed, but what we have to do is get the actual size of the keyboard; that way we can resize the table view to compensate for the size of the keyboard.
So first we’re going to declare a CGRect keyboardFrame and then I'll say note.userInfo[UIKeyboardFrameEndUserInfoKey].
All right, great! And now we’re going to have another set of brackets around that, getValue:&keyboardFrame. And addressOf just means putting the &.
Basically what this will do is it’ll set keyboardFrame in a very unconventional way. It’ll set keyboardFrame to be the frame of the keyboard that has been displayed and now we can essentially resize the table view to have less height to be shorter.
So that way it doesn’t block this. So we’re going to say CGRect tableViewFrame = tableView.frame, and let me fix my capitalization issue there.
Now tableViewFrame.size.height -= keyboardFrame.size.height, and basically what this will do is it’ll subtract the height of the keyboard from the height of the table view, so that way the table view is just shorter to fit the new height of the screen without the keyboard.
And now we’ll say tableView.frame = tableViewFrame to actually update the frame.
Okay, and now if we run it, at first it’ll seem like we have pretty promising results.
So let me just show you if we do that, now we can scroll to the bottom. But now, let’s say I hide my keyboard, boom, where’s the other half of the table view?
You know, now it's only here because when the keyboard is hidden it doesn't resize itself to cover the full screen again, so we have to do another thing for when the keyboard goes away.
So that way we can resize it to be the full height again. So we’re going to do another thing right under this NSNotificationCenter thing.
And in fact, I can actually just copy this line of code and change one thing, so let me do that. Let me just change the selector to keyboardHidden and keyboardWillHideNotification.
Okay, and now we’re just going to implement our keyboard hidden note.
Okay, and we’ll say tableView.frame = self.view.bounds or self.bounds, and basically that will just put the entire keyboard or the table view to cover the entire screen again.
So that’s all we need to do there. And now if we go ahead and stop this and we run it, you will see that search still works, scrolling works, and now if here we hit enter, it goes back and covers the entire screen again.
So that’s pretty much all I got. I wanted to show you guys in this tutorial. Hope you learned something. Any questions leave comments in the box below.
Don’t forget to like this video if you enjoyed it. So thanks for watching Maads 101, subscribe, and goodbye!