iOS Search bar with Realm Database
The search bar is quite a common feature to have in an iOS app whenever you need to show data in a listview. And today I'm going to demonstrate how to add a search bar using a navigation bar button and filter the listview data which uses a Realm database.
Now first, let's go through the code to add a search bar using a button I'm placing on the right side of the navigation bar.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UISearchDisplayDelegate {
private lazy var dataSearchBar: UISearchBar = {
let searchBar = UISearchBar(frame: CGRect(x: 0.0, y: 0.0, width: self.view.frame.size.width, height: 44.0))
searchBar.placeholder = "Search..."
return searchBar
}()
@IBOutlet weak var tblView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "search"), style: .plain, target: self, action: #selector(showOrHideSearchBar))
tblView.tableHeaderView = dataSearchBar
dataSearchBar.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
var contentOffset = tblView.contentOffset
contentOffset.y = contentOffset.y + (tblView.tableHeaderView?.frame ?? CGRect()).height
tblView.contentOffset = contentOffset
}
var isSearchShown = false
@objc func showOrHideSearchBar(_ sender: Any) {
if (isSearchShown) {
var contentOffset = tblView.contentOffset
contentOffset.y = contentOffset.y + (tblView.tableHeaderView?.frame ?? CGRect()).height
tblView.contentOffset = contentOffset
} else {
let indexPath = IndexPath(item: 1, section: 0)
tblView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
isSearchShown = !isSearchShown
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
// To Do
}
}
Now, if we go through the code, a UISearchBar is initialised in the view controller and is added as a header view for the table view. This is specifically done to show/hide the search bar on top of the table view. You can add a search bar anywhere in the view controller as you like.
And to hide the search bar at the start of the screen, I'm scrolling the table view to the height of the search bar in viewWillAppear. And the method showOrHideSearchBar() handles the duty to show/hide the search bar, which is again adjusting the table view height on the inside.
Now let's get to the part that filters the data for the table view.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if (searchText != "") {
listOfEmployees = realm.objects(Employee.self).filter("name contains[c] %@", searchText)
tblView.reloadData()
} else {
listOfEmployees = realm.objects(Employee.self)
tblView.reloadData()
}
}
That's all we'll need, now an interesting thing here to notice is how we use the filter with Realm, the contains[c] filter allows us to search to be case insensitive and the text can be present anywhere in the results string. For example, searching for son will result in listing ["Johnson Jeff", "Williamson", "Son heung min"].
Now the full code looks something like this :
//
// ViewController.swift
// SearchBarDemo
//
// Created by apple on 01/07/23.
//
import UIKit
import RealmSwift
@objcMembers class Employee: Object {
dynamic var name: String = ""
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UISearchDisplayDelegate {
var listOfEmployees: Results<Employee>?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.listOfEmployees?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! tableCell
cell.lblName.text = self.listOfEmployees?[indexPath.row].name
return cell
}
@IBOutlet weak var tblView: UITableView!
var realm: Realm = try! Realm()
private lazy var dataSearchBar: UISearchBar = {
let searchBar = UISearchBar(frame: CGRect(x: 0.0, y: 0.0, width: self.view.frame.size.width, height: 44.0))
searchBar.placeholder = "Search..."
return searchBar
}()
override func viewDidLoad() {
super.viewDidLoad()
title = "SearchBarDemo"
self.listOfEmployees = realm.objects(Employee.self)
self.tblView.dataSource = self
self.tblView.delegate = self
navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "search"), style: .plain, target: self, action: #selector(showOrHideSearchBar))
tblView.tableHeaderView = dataSearchBar
dataSearchBar.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
var contentOffset = tblView.contentOffset
contentOffset.y = contentOffset.y + (tblView.tableHeaderView?.frame ?? CGRect()).height
tblView.contentOffset = contentOffset
}
var isSearchShown = false
@objc func showOrHideSearchBar(_ sender: Any) {
if (isSearchShown) {
var contentOffset = tblView.contentOffset
contentOffset.y = contentOffset.y + (tblView.tableHeaderView?.frame ?? CGRect()).height
tblView.contentOffset = contentOffset
} else {
let indexPath = IndexPath(item: 1, section: 0)
tblView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
isSearchShown = !isSearchShown
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if (searchText != "") {
listOfEmployees = realm.objects(Employee.self).filter("name contains[c] %@", searchText)
tblView.reloadData()
} else {
listOfEmployees = realm.objects(Employee.self)
tblView.reloadData()
}
}
}
class tableCell : UITableViewCell
{
@IBOutlet weak var lblName: UILabel!
}