iOS UISearchBar with UITableView example

UISearchBar class provides a text field for entering text, a search button, a bookmark button, and a cancel button. With the help of the UISearchBarDelegate protocol you can implement the actions that will perform search on a database locally or send a request to the remote server when text is entered or buttons are clicked. In this example we implement the search bar to find a country by its name. The search request is sent to the remote server where a Java Servlet performs a simple query over the MySQL database to return a JSON array that is then displayed to the user using the Table view.

iOS UISearchBar with UITableView example iOS UISearchBar query with results in TableView iOS UISearchBar query with No Results

Java Servlet backend for remote search - CountrySearch.java

package com.as400samplecode;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

import com.as400samplecode.util.Country;
import com.google.gson.Gson;
import com.google.gson.JsonObject;

public class CountrySearch extends HttpServlet {
 
 private static final long serialVersionUID = 1L;
 
 public CountrySearch() {
  super();
 }

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  doPost(request,response);
 }

 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  PrintWriter out = response.getWriter();
  
  //check of query data is sent
  String queryString = request.getParameter("queryString");
  if(queryString == null){
   queryString = "";
  }
  
  //get list of countries and prepare JSON array to send back
  ArrayList<Country> countryList = new ArrayList<Country>();
  Connection conn = null;             
  PreparedStatement stmt = null;      
  String sql = null;
  
  try {       
   Context ctx = (Context) new InitialContext().lookup("java:comp/env");
   conn = ((DataSource) ctx.lookup("jdbc/mysql")).getConnection(); 

   sql = "Select * from country where code <> 'CIV' "; 
   if(!queryString.trim().equals("")){
    sql += "and lcase(name) like ?";
   }
   
   stmt = conn.prepareStatement(sql);
   if(!queryString.trim().equals("")){
    stmt.setString(1, "%" + queryString.trim().toLowerCase() + "%" );
   }
   
   ResultSet rs = stmt.executeQuery();  
   while(rs.next()){ 
    
    Country country = new Country();
    country.setCode(rs.getString("code").trim());
    country.setName(rs.getString("name").trim());
    countryList.add(country);
    
   }                                                                          
   rs.close();                                                                
   
   stmt.close();                                                              
   stmt = null;                                                               
   conn.close();                                                              
   conn = null;  
   
  }                                                                
  catch(Exception e){
   e.printStackTrace();
  }                       

  finally {                                                        
   
   if (stmt != null) {                                             
    try {                                                          
     stmt.close();                                                 
    } catch (SQLException sqlex) {                                 
     // ignore -- as we can't do anything about it here            
    }                                                              

    stmt = null;                                             
   }                                                         

   if (conn != null) {                                       
    try {                                                    
     conn.close();                                           
    } catch (SQLException sqlex) {                           
     // ignore -- as we can't do anything about it here      
    }                                                        

    conn = null;                                             
   }                                                         
  } 
  
  Gson gson = new Gson();
  JsonObject myObj = new JsonObject();
  myObj.add("countryList", gson.toJsonTree(countryList));
  myObj.addProperty("success", true);
  out.println(myObj.toString());

 }
 
}

Interface file for the App Delegate - MySearchBarAppDelegate.h

#import <UIKit/UIKit.h>

@class MySearchBarViewController;

@interface MySearchBarAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) MySearchBarViewController *viewController;

@end

Implementation file for the App Delegate - MySearchBarAppDelegate.m

#import "MySearchBarAppDelegate.h"
#import "MySearchBarViewController.h"

@implementation MySearchBarAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.viewController = [[MySearchBarViewController alloc]
                           initWithNibName:@"MySearchBarViewController" bundle:nil];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    return YES;
}

@end

Interface file for the UISearchBar View Controller - MySearchBarViewController.h

#import <UIKit/UIKit.h>

@interface MySearchBarViewController : UIViewController <UISearchBarDelegate,
                                UITableViewDelegate,UITableViewDataSource>

@property (nonatomic, strong) UISearchBar *mySearchBar;
@property (nonatomic, strong) UITableView *myTableView;
@property (nonatomic, strong) NSMutableArray *countryList;

@property (nonatomic, strong) NSString *queryString;

@end

Implementation file for the UISearchBar View Controller - MySearchBarViewController.m

#import "MySearchBarViewController.h"

@interface MySearchBarViewController ()

@end

@implementation MySearchBarViewController

@synthesize mySearchBar;
@synthesize myTableView;
@synthesize countryList;
@synthesize queryString;

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //create a search bar and add to the top of the screen
 CGRect myFrame = CGRectMake(self.view.bounds.origin.x, self.view.bounds.origin.y,
                                self.view.bounds.size.width, 44.0f);
    self.mySearchBar = [[UISearchBar alloc] initWithFrame:myFrame];
    //set the delegate to self so we can listen for events
    self.mySearchBar.delegate = self;
    //display the cancel button next to the search bar
    self.mySearchBar.showsCancelButton = YES;
    //add the search bar to the view
    [self.view addSubview:self.mySearchBar];
    
    //set the frame for the table view
    myFrame.origin.y += 44;
    myFrame.size.height = self.view.bounds.size.height - 44;
    self.myTableView = [[UITableView alloc] initWithFrame:myFrame
                                                    style:UITableViewStylePlain];
    //set the table view delegate and the data source
    self.myTableView.delegate = self;
    self.myTableView.dataSource = self;
    //set table view resize attribute
    self.myTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
                                        UIViewAutoresizingFlexibleHeight;
    //set background view and color
    self.myTableView.backgroundColor = [UIColor whiteColor];
    self.myTableView.backgroundView = nil;
    //add table view to the main view
    [self.view addSubview:self.myTableView];
    
    //execute the search without any query to display all countries
    [self handleSearch:self.mySearchBar];
}

//search button was tapped
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
    [self handleSearch:searchBar];
}

//user finished editing the search text
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
    [self handleSearch:searchBar];
}

//do our search on the remote server using HTTP request
- (void)handleSearch:(UISearchBar *)searchBar {
    
    //check what was passed as the query String and get rid of the keyboard
    NSLog(@"User searched for %@", searchBar.text);
    self.queryString = searchBar.text;
    [searchBar resignFirstResponder];
    
    //setup the remote server URI
    NSString *hostServer = @"http://demo.mysamplecode.com/Servlets_JSP/";
    NSString *myUrlString = [NSString stringWithFormat:@"%@CountrySearch",hostServer];
    
    //pass the query String in the body of the HTTP post
    NSString *body;
    if(self.queryString){
        body =  [NSString stringWithFormat:@"queryString=%@", self.queryString];
    }
    NSURL *myUrl = [NSURL URLWithString:myUrlString];
    
    //make the HTTP request
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:myUrl];
    [urlRequest setTimeoutInterval:60.0f];
    [urlRequest setHTTPMethod:@"POST"];
    [urlRequest setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [NSURLConnection
     sendAsynchronousRequest:urlRequest
     queue:queue
     completionHandler:^(NSURLResponse *response,
                         NSData *data,
                         NSError *error) {
         //we got something in reponse to our request lets go ahead and process this
         if ([data length] >0 && error == nil){
                 [self parseResponse:data];
         }
         else if ([data length] == 0 && error == nil){
             NSLog(@"Empty Response, not sure why?");
         }
         else if (error != nil){
             NSLog(@"Not again, what is the error = %@", error);
         }
     }];
    
}

//parse our JSON response from the server and load the NSMutableArray of countries
- (void) parseResponse:(NSData *) data {
    
    NSString *myData = [[NSString alloc] initWithData:data
                                             encoding:NSUTF8StringEncoding];
    NSLog(@"JSON data = %@", myData);
    NSError *error = nil;
    
    id jsonObject = [NSJSONSerialization
                     JSONObjectWithData:data
                     options:NSJSONReadingAllowFragments
                     error:&error];
    if (jsonObject != nil && error == nil){
        NSLog(@"Successfully deserialized...");
        
        NSNumber *success = [jsonObject objectForKey:@"success"];
        if([success boolValue] == YES){
            
            self.countryList = [jsonObject objectForKey:@"countryList"];
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.myTableView reloadData];
            });
        }
        else {
            [self.navigationController popToRootViewControllerAnimated:YES];
        }
    }
    
}

//number of rows in a given section of a table view
- (NSInteger)tableView:(UITableView *)tableView
 numberOfRowsInSection:(NSInteger)section{
    
    NSInteger numberOfRows = 0;
    //get the count from the array
    if ([tableView isEqual:self.myTableView]){
        numberOfRows = self.countryList.count;
    }
    //if user searched for something and found nothing just add a row to display a message
    if(numberOfRows == 0 && [self.queryString length] > 0){
        numberOfRows = 1;
    }
    NSLog(@"Rows: %i", numberOfRows);
    return numberOfRows;
}


//asks the data source for a cell to insert in a particular location of the table view
- (UITableViewCell *) tableView:(UITableView *)tableView
          cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    UITableViewCell *myCellView = nil;
    
    if ([tableView isEqual:self.myTableView]){
        
        static NSString *TableViewCellIdentifier = @"CountryCells";
        //create a reusable table-view cell object located by its identifier
        myCellView = [tableView dequeueReusableCellWithIdentifier:TableViewCellIdentifier];
        if (myCellView == nil){
            myCellView = [[UITableViewCell alloc]
                          initWithStyle:UITableViewCellStyleValue1
                          reuseIdentifier:TableViewCellIdentifier];
        }
        
        //if there are countries to display
        if(self.countryList.count > 0){
            NSDictionary *countryInfo = [self.countryList objectAtIndex:indexPath.row];
            NSLog(@"Country Info = %@",countryInfo);
            
            NSString *countryCode = [countryInfo  valueForKey:@"code"];
            NSString *countryName = [countryInfo  valueForKey:@"name"];
            myCellView.textLabel.text = [NSString stringWithFormat:@"%@",countryName];
            myCellView.detailTextLabel.text = countryCode;
        }
        //display message to user
        else {
            myCellView.textLabel.text = @"No Results found, try again!";
            myCellView.detailTextLabel.text = @"";
        }
        
        //set the table view cell style
        [myCellView setSelectionStyle:UITableViewCellSelectionStyleNone];
        
    }
    return myCellView;
}

//user tapped on the cancel button
- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar {
    NSLog(@"User canceled search");
    [searchBar resignFirstResponder];
}


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

Reference


1 comment:

rithiaanandh said...

Thanks for sharing this informative blog.This is one of the best resources I have found in quite some time. Nicely written and great info. I really cannot thank you enough for sharing.I’ve been commenting a lot on a few blogs recently, but I hadn’t thought about my approach until you brought it up.

Post a Comment

NO JUNK, Please try to keep this clean and related to the topic at hand.
Comments are for users to ask questions, collaborate or improve on existing.