How to create a scope search using tableview and searchbarcontroller in swift3 - uitableview

Hello all have designed a scope search using tableview and serchbarcontroller. To achieve this have used below code but somehow its not returning me the actual output. Hoping for the help. Thank you.
output :
here is my output's ScreenShot
code :
import UIKit
class SearchBookVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating, UISearchBarDelegate {
#IBOutlet weak var tableview: UITableView!
struct Books {
var name = String()
var board = String()
var classname = String()
var area = String()
var city = String()
}
var books = [Books(name: "Physics", board: "CBSE",classname:"XI",area:"Karnataka",city:"Bangalore"),
Books(name:"English",board:"CBSE",classname:"X",area:"Maharashtra",city:"pune"),
Books(name:"biology",board:"IB",classname:"XII",area:"Gujarat",city:"Rajkot"),
Books(name:"chemistry",board:"IB",classname:"X",area:"Gujarat",city:"Ahmedabad"),
Books(name:"Maths",board:"ICSE",classname:"IX",area:"Maharashtra",city:"Mumbai"),
Books(name:"Science",board:"ICSE",classname:"XII",area:"Karnataka",city:"Mysore")
]
var filteredBooks = [Books]()
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
filteredBooks = books
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableview.tableHeaderView = searchController.searchBar
searchController.searchBar.scopeButtonTitles = ["All","Name", "Board", "Class", "Area","City"]
searchController.searchBar.delegate = self
self.tableview.register(mycell.self, forCellReuseIdentifier: "cell")
// Do any additional setup after loading the view.
}
func applySearch(searchText: String, scope: String = "All") {
if searchController.searchBar.text! == ""
{
filteredBooks = books.filter { book in
let nameofbook = ( scope == "All") || (book.name == scope) || (book.board == scope) || (book.classname == scope) || (book.area == scope) || (book.city == scope)
return nameofbook
}
}
else
{
filteredBooks = books.filter { book in
let nameofbook = ( scope == "All") || (book.name == scope) || (book.board == scope) || (book.classname == scope) || (book.area == scope) || (book.city == scope)
return nameofbook && book.name.lowercased().contains(searchText.lowercased()) && book.board.lowercased().contains(searchText.lowercased()) && book.classname.lowercased().contains(searchText.lowercased()) && book.area.lowercased().contains(searchText.lowercased()) && book.city.lowercased().contains(searchText.lowercased())
}
}
self.tableview.reloadData()
}
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let selectedScope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
applySearch(searchText: searchController.searchBar.text!,scope: selectedScope)
}
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
applySearch(searchText: searchController.searchBar.text!,scope: searchBar.scopeButtonTitles![selectedScope])
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.filteredBooks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! mycell
cell.bookname?.text = self.filteredBooks[indexPath.row].name
cell.Boardname?.text = self.filteredBooks[indexPath.row].board
cell.classtitle?.text = self.filteredBooks[indexPath.row].classname
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Row \(indexPath.row) selected")
}
}

Can you try it this way:
Make an array of auto-completion text-field, one for each of the data source.
Let these text-fields to be invisible at first.
After you select values and click the search button, let the corresponding text-fields to be visible.
Not sure if it will meet your requirement, please leave comments if you have any problem.

Related

searchResults not showing correct words

I have a searchResultsViewController in my iOS application that displays an array of data for the user to be able to search through. When I try to search a random letter, lets say P for instance it does not show any of the words containing P.
the code that I used to create this searchResults is,
var array = ["Assembly", "Auto Care", "Electronic Help", "Item Delivery", "Handyman", "House Chores", "Junk Removal", "Lawn & Yard Care", "Moving", "Painting", "Pet Care", "Seasonal Work"]
var selectedItems = [String]()
var searchController = UISearchController()
var filteredArray = [String]()
var resultsController = UITableViewController()
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: resultsController)
tableView.tableHeaderView = searchController.searchBar
searchController.searchResultsUpdater = self
resultsController.tableView.delegate = self
resultsController.tableView.dataSource = self
searchController.searchBar.showsCancelButton = true
searchController.searchBar.showsScopeBar = true
searchController.searchBar.delegate = self
let attributes = [NSAttributedString.Key.foregroundColor: GREEN_Theme]
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes(attributes, for: UIControl.State.normal)
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Done"
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
done()
}
func updateSearchResults(for searchController: UISearchController) {
filteredArray = array.filter({ (array:String) -> Bool in
if array.contains(searchController.searchBar.text!) {
return true
} else {
return false
}
})
resultsController.tableView.reloadData()
searchController.automaticallyShowsCancelButton = true
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedItems.contains(array[indexPath.row]) {
selectedItems.remove(at: selectedItems.firstIndex(of: array[indexPath.row])!)
tableView.cellForRow(at: indexPath)?.accessoryType = .none
} else {
selectedItems.append(array[indexPath.row])
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
tableView.reloadData()
print(selectedItems)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == resultsController.tableView {
return filteredArray.count
} else {
return array.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = array[indexPath.row]
if selectedItems.contains(array[indexPath.row]) {
cell.accessoryType = .checkmark
}else{
cell.accessoryType = .none
}
return cell
}
any thoughts?
Use this function to filter stuff:
extension SearchVC: UISearchBarDelegate{
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
fetchedData = []
if searchText == ""{
fetchedData = items
} else {
for words in items{
if
words.item.lowercased().contains(searchText.lowercased()){
filteredData.append(words)
}
}
}
table.reloadData()
}
}
Where fetchedData is an empty string array and items is your array.
If the search bar is empty fetchedData will be filled with all of your items, else just with the matched ones.
Now, the most important thing to do is to use fetchedData instead of items to display the results and the count properly. So, for instance:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return filteredData.count
}
Furthermore, as other users pointed out in the comments, you should really check your cellForRowAt. Try this link: https://stackoverflow.com/a/34345245/10408872

Create SearchController for reusable use

Is it possible to create UISearchController for reusable use in many classes?
To use a minimum of code in new classes.
Now I have code in all classes, but I want to use minimum code in all classes to call searchController:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
#IBOutlet weak var tableView: UITableView!
var photoBook: [PhotoBooking] = []
var filteredBook: [PhotoBooking] = []
private var searchController = UISearchController(searchResultsController: nil)
private var searchBarIsEmpty: Bool {
guard let text = searchController.searchBar.text else { return false }
return text.isEmpty
}
private var isFiltering: Bool {
return searchController.isActive && !searchBarIsEmpty
}
override func viewDidLoad() {
super.viewDidLoad()
configureSearchController()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering {
return filteredBook.count
}
return photoBook.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
var pBook: PhotoBooking
if isFiltering {
pBook = filteredBook[indexPath.row]
} else {
pBook = photoBook[indexPath.row]
}
cell.textLabel?.text = pBook.invoiceID
cell.detailTextLabel?.text = pBook.contactInfo["surname"] as! String
return cell
}
private func configureSearchController() {
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.searchBar.barTintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.delegate = self
tableView.tableHeaderView = searchController.searchBar
definesPresentationContext = true
}
}
extension AllPhotoBookingsViewController: UISearchResultsUpdating {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
filterContentForSearchText(searchBar.text!)
tableView.reloadData()
}
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
tableView.reloadData()
}
private func filterContentForSearchText(_ searchText: String) {
filteredBook = photoBook.filter({ (book: PhotoBooking) -> Bool in
let name = book.contactInfo["name"] as! String
let surname = book.contactInfo["surname"] as! String
let invoice = book.invoiceID
return invoice.localizedCaseInsensitiveContains(searchText) ||
name.localizedCaseInsensitiveContains(searchText) ||
surname.localizedCaseInsensitiveContains(searchText)
})
tableView.reloadData()
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
navigationController?.hidesBarsOnSwipe = false
}
}
Maybe need use protocol, but I don't understand how to use correctly protocol.
There are many ways of doing this, using protocols is a great approach, but you could also subclass UIViewController and make a BaseSearchableViewController where you would add all the functionality you need for your search, then, you just subclass from BaseSearchableViewController instead of UIViewController.

SWIFT 4 UISearchBar and UITableView

I started a table view with a list of universities and created a search bar to tag along with it. The search bar works but only if I type in the name of the school exactly how it is. Is there a way I can change the it to search any part of the name and get the same results? Here's the code that I have set up.
#IBOutlet weak var schoolSearch: UISearchBar!
#IBOutlet weak var tblView: UITableView!
let schoolnames = ["Long Beach City College LAC", "California State University, Bakersfield", ...]
var searchedSchool = [String]()
var searching = false
override func viewDidLoad() {
super.viewDidLoad()
schoolSearch.delegate = self
self.tblView.delegate = self
self.tblView.reloadData()
}
extension ChooseSchool: UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return searchedSchool.count
} else {
return schoolnames.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableViewCell
cell?.img.image = UIImage(named: schoolnames[indexPath.row])
cell?.lbl.text = schoolnames[indexPath.row]
_ = tableView.dequeueReusableCell(withIdentifier: "cell")
if searching {
cell?.textLabel?.text = searchedSchool[indexPath.row]
} else {
cell?.textLabel?.text = schoolnames[indexPath.row]
}
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "TestController") as? TestController
vc?.schoolnames = schoolnames[indexPath.row]
navigationController?.pushViewController(vc!, animated: true)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchedSchool = schoolnames.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()})
searching = true
tblView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searching = false
searchBar.text = ""
tblView.reloadData()
}
}
Replace
searchedSchool = schoolnames.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()})
with
searchedSchool = schoolnames.filter { $0.range(of: searchText, options: .caseInsensitive) != nil }
I think you have to make your searchBar implement the containsString method to achieve what you need. For reference look at this link

Easiest way to sort data from UItableview in alphabetical order

I have had a look but cannot find a suitable answer.
I have a tableview populated with the names of drugs. The drugs have been entered as they have been encountered from text into Xcode as shown below.
class MasterViewController: UITableViewController {
// MARK: - Properties
var detailViewController: DetailViewController? = nil
var drugs = [Drug]()
var filtereddrugs = [Drug]()
let searchController = UISearchController(searchResultsController: nil)
// MARK: - View Setup
override func viewDidLoad() {
super.viewDidLoad()
// Setup the Search Controller
searchController.searchResultsUpdater = self
searchController.searchBar.delegate = self
definesPresentationContext = true
searchController.dimsBackgroundDuringPresentation = false
// Setup the Scope Bar
searchController.searchBar.scopeButtonTitles = ["All", "Green", "Blue", "Amber", "Red", "Black"]
tableView.tableHeaderView = searchController.searchBar
drugs = [
Drug(category:"Green", name:"Magnesium Trisilicate"),
Drug(category:"Green", name:"Co-magaldrox (Mucogel)"),
Drug(category:"Green", name: "Gastrocote"),
Drug(category:"Green", name:"Infant Gaviscon"),
Drug(category:"Green", name:"Peptac"),
Drug(category:"Green", name:"Mebeverine"),
Drug(category:"Green", name:"Fybogel Mebeverine"),
Drug(category:"Black", name:"Peppermint oil")]
if let splitViewController = splitViewController {
let controllers = splitViewController.viewControllers
detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
}
override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table View
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive && searchController.searchBar.text != "" {
return filtereddrugs.count
}
return drugs.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let drug: Drug
if searchController.isActive && searchController.searchBar.text != "" {
drug = filtereddrugs[(indexPath as NSIndexPath).row]
} else {
drug = drugs[(indexPath as NSIndexPath).row]
}
cell.textLabel!.text = drug.name
cell.detailTextLabel!.text = drug.category
return cell
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filtereddrugs = drugs.filter({( drug : Drug) -> Bool in
let categoryMatch = (scope == "All") || (drug.category == scope)
return categoryMatch && drug.name.lowercased().contains(searchText.lowercased())
})
tableView.reloadData()
}
// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let drug: Drug
if searchController.isActive && searchController.searchBar.text != "" {
drug = filtereddrugs[(indexPath as NSIndexPath).row]
} else {
drug = drugs[(indexPath as NSIndexPath).row]
}
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
controller.detailDrug = drug
controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
}
extension MasterViewController: UISearchBarDelegate {
// MARK: - UISearchBar Delegate
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}
extension MasterViewController: UISearchResultsUpdating {
// MARK: - UISearchResultsUpdating Delegate
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}
}
This list of drugs is not the full list, in fact in contains many many more, too many to sort manually! And over the next few weeks I will be entering more.
I don't have the time or will power to change it to appear in the table view in alphabetical order and I am wondering could a few lines of code solve the issue?
Cheers in advance,
Mark
Try adding this, it will sort drugs whenever the property is set.
var drugs = [Drug]() {
didSet {
drugs.sort { $0.name < $1.name }
}
}
What is the purpose of willSet and didSet in Swift?
What does $0 and $1 mean in Swift Closures?

Search Bar on TableView Does not Work

I'm trying to add a search bar to a simple table view consisting of 7 cells of names and small description for each name.
As in the image here:
I made a class in swift file called Business, that has two attributes: Name and Des.
Here's the code in the view controller:
class FirstViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var TableView: UITableView!
var B = [Business]() //candies
var filteredNames = [Business]()
let searchController = UISearchController(searchResultsController: nil)
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredNames = B.filter { Bu in
return Bu.Name.lowercaseString.containsString(searchText.lowercaseString)
}
TableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
B = [
Business(Name:"Mariah", Des:"I'm Here to help"),
Business(Name:"Nada", Des:"Hi"),
Business(Name:"Atheer", Des:"Hello"),
Business(Name:"Lojian", Des:"I can Help you"),
Business(Name:"Nadya", Des:"Hayat"),
Business(Name:"Omnia", Des:"Yahoo"),
Business(Name:"Eman", Des:"I have amazing stuff"),
Business(Name:"Amani", Des:"Yess")
]
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
TableView.tableHeaderView = searchController.searchBar
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.active && searchController.searchBar.text != "" {
return filteredNames.count
}
return B.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.TableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CellTableViewCell
cell.NameLabel.text = B[indexPath.row].Name
cell.DescriptionLabel.text = B[indexPath.row].Des
let Bu: Business
if searchController.active && searchController.searchBar.text != "" {
Bu = filteredNames[indexPath.row]
} else {
Bu = B[indexPath.row]
}
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell
}
}
extension FirstViewController: UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController:
(UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
}
I followed this tutorial to do that:
https://www.raywenderlich.com/113772/uisearchcontroller-tutorial
I don't know whay when I tried to search in simulator the result is always the first cell: Mariah
What's wrong with the code?
You don't use the search result to populate the cells. Replace you cellForRowAtIndexPath with this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.TableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CellTableViewCell
let Bu: Business
if searchController.active && searchController.searchBar.text != "" {
Bu = filteredNames[indexPath.row]
} else {
Bu = B[indexPath.row]
}
cell.NameLabel.text = Bu.Name
cell.DescriptionLabel.text = Bu.Des
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell
}
And, don't use capital first letters for properties.

Resources