Search Bar on TableView Does not Work - ios

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.

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

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

filtering and displaying searchbar results from firebase database

I am just starting to learn swift and firebase. I want to add a search bar that will allow users to search through my firebase database. This is what I want to get
I have added the searchbar, what I'm having problem with is the display of search result.
I created a container view that include Name, subdescription and logo like the image above and then set them up with this function
func searchResultContainer(){
searchResultView.addSubview(businesslogoView)
searchResultView.addSubview(businessNameLabel)
searchResultView.addSubview(businessSectorLabel)
//need x. y, width, height constraints for searchResult
searchResultView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
searchResultView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
searchResultView.heightAnchor.constraint(equalToConstant: 220).isActive = true
}
I then append the searchResult view to var bussinesarray. and then insert it into the tableview. Please see my code below
var businessArray = [NSDictionary]()
var filterBusiness = [NSDictionary]()
var ref : FIRDatabaseReference!
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.insertRows(at: [IndexPath(row: self.businessArray.count-1, section: 0)], with: UITableViewRowAnimation.automatic)
ref.child("Businesses").queryOrdered(byChild: "Basic-Info/business").observe(.childAdded, with: { (snapshot) in
view.addSubview(searchResultView)
searchResultContainer()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// if searchbar is not empty "", then return filtered businesses if the user is not typing anything return all businesses.
if searchController.isActive && searchController.searchBar.text !=
""{
return filterBusiness.count
}
return self.businessArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let business : NSDictionary?
if searchController.isActive && searchController.searchBar.text !=
""{
business = filterBusiness[indexPath.row]
}
else
{
business = self.businessArray[indexPath.row]
}
cell.textLabel?.text = business?["Business"] as? String
cell.detailTextLabel?.text = business?["handle"] as? String
return cell
}
func filterContent (searchText:String) {
self.filterBusiness = self.businessArray.filter{ Businesses in
let businessName = Businesses["Business"] as? String
return(businessName?.contains(searchText.lowercased()))!
}
tableView.reloadData()
}
func updateSearchResults(for searchController: UISearchController) {
// update the search results
filterContent(searchText: self.searchController.searchBar.text!)
}
I am not getting the search result from firebase DB, how do I correctly implement the search result from firebase DB? I am building everything programmatically, please a sample code with be greatly appreciated.
This tutorial was a great help for me in figuring out a similar implementation, see the code near the bottom of the tutorial.
http://shrikar.com/swift-ios-tutorial-uisearchbar-and-uisearchbardelegate/
Code adjustments beyond this tutorial included the below code. I still have some clean up that could be done around the if/else section however the two critical concepts for me was using the model and getting the target correct with: let temp: NSString = text.EntityName! as NSString
Model file:
class Dealer: NSObject{
var DealerNumber: String?
var EntityName: String?
//matchup all other firebase data fields
}
ViewController Adjustments
var dealerList = [Dealer]()
var filterDealers = [Dealer]()
---
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filterDealers = dealerList.filter({ (text) -> Bool in
let temp: NSString = text.EntityName! as NSString
let range = temp.range(of: searchText, options: NSString.CompareOptions.caseInsensitive)
return range.location != NSNotFound
})
if(filterDealers.count == 0){
searchActive = false;
} else {
searchActive = true;
}
refreshTable()
}
----
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
var cell:UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier)
if(searchActive){
cell?.textLabel?.text = filterDealers[indexPath.row].EntityName
cell?.detailTextLabel?.text = filterDealers[indexPath.row].DealerNumber
} else {
cell?.textLabel?.text = dealerList[indexPath.row].EntityName
cell?.detailTextLabel?.text = dealerList[indexPath.row].DealerNumber
}
return cell!;
}

Using searchController to filter tableView, but the tableView isn't updating

I have a UITableViewController that is displaying the titles of Tags I created. When I first navigate to the UITableViewController, it displays the Array of Tags just fine, but when I use the UISearchController to filter through Tags, the Array I created to store the filtered results updates and holds the correct data, but the TableView doesn't change. here are the two functions that are most likely causing the problem, but just in case, I will have the entire class (not long) down below.
numberOfRowsInSection:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchController.searchBar.text != "") {
return filteredTags.count
}
return Tags.count
}
cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tagcell", for: indexPath) as! TagCell
var text = ""
if (searchController.searchBar.text != ""){
text = filteredTags[indexPath.row].title
} else {
text = Tags[indexPath.row].title
}
cell.cellLabel.text = text
return cell
}
Whole Class:
class TagCell: UITableViewCell{
#IBOutlet weak var cellLabel: UILabel!
}
class TagTableVC: UITableViewController{
//Table Content
var Tags: [Tag] = [globTS.animals, globTS.civilrights, globTS.guncontrol, globTS.gunrights, globTS.LGBTQ, globTS.prochoice, globTS.prolife]
var filteredTags = [Tag]()
//Searchbar Initialization
let searchController = UISearchController(searchResultsController: nil)
//Required Functions
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchController.searchBar.text != "") {
return filteredTags.count
}
return Tags.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tagcell", for: indexPath) as! TagCell
var text = ""
if (searchController.searchBar.text != ""){
text = filteredTags[indexPath.row].title
} else {
text = Tags[indexPath.row].title
}
cell.cellLabel.text = text
return cell
}
//Filters Tags array into Filtered array based on search query
func filterContentForSearchText(searchText: String, scope: String = "All"){
filteredTags = Tags.filter{ $0.title.lowercased().contains(searchText.lowercased())}
}
}
extension TagTableVC: UISearchResultsUpdating {
//calls the filter function everytime the searchbar is activated
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchText: searchController.searchBar.text!)
}
}
After reevaluating the filteredTags, you should call reloadData on your tableview
func filterContentForSearchText(searchText: String, scope: String = "All"){
filteredTags = Tags.filter{ $0.title.lowercased().contains(searchText.lowercased())}
self.tableView.reloadData()
}

How can I make my UISearchController show the cells underneath?

I have a UITableViewController (with 6 cells) and a UISearchController. Whenever I tap on the search bar, the table view cells disappear and are replaced with an empty tableView until I start typing something and the search results appear. How can I make it so that cells stay underneath when I tap the search bar?
Here is my code pertaining to the SearchController:
In viewDidLoad():
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = true
controller.searchBar.sizeToFit()
controller.searchBar.barTintColor = UIColor(red: 240/255, green: 240/255, blue: 240/255, alpha: 1)
self.tableView.tableHeaderView = controller.searchBar
self.definesPresentationContext = true
return controller
})()
The rest:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (self.resultSearchController.active) {
return self.filteredTableData.count
} else {
return self.items.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TableViewCell
var item : TextCell
if (self.resultSearchController.active) {
item = filteredTableData[indexPath.row]
return cell
} else {
item = items[indexPath.row]
cell.userText.text = item.userText
cell.userPreferredName.text = item.userPreferredName
cell.userName.text = item.userName
cell.userImage.layer.cornerRadius = 23.75
cell.userImage.clipsToBounds = true
cell.userImage.layer.borderWidth = 0.3
cell.userImage.layer.borderColor = UIColor.lightGrayColor().CGColor
cell.time.text = "\((indexPath.row + 1) * 3)m"
return cell
}
}
}
extension TableViewController: UITableViewDelegate {
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
extension TableViewController: UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
var filteredArray : [TextCell] = [TextCell]()
for textCell in self.items
{
if (textCell.userName.rangeOfString(searchController.searchBar.text) != nil ||
textCell.userPreferredName.rangeOfString(searchController.searchBar.text) != nil ||
textCell.userText.rangeOfString(searchController.searchBar.text) != nil)
{
filteredArray.append(textCell)
}
}
filteredTableData = filteredArray
self.tableView.reloadData()
}
}
Screenshots:
Bonus:
Is there any way of making those cells selectable?
If the searchText is empty, then set the filteredTableData to items
extension TableViewController: UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
var filteredArray : [TextCell] = [TextCell]()
if searchController.searchBar.text == "" {
filteredArray = self.items
} else {
for textCell in self.items
{
if (textCell.userName.rangeOfString(searchController.searchBar.text) != nil ||
textCell.userPreferredName.rangeOfString(searchController.searchBar.text) != nil ||
textCell.userText.rangeOfString(searchController.searchBar.text) != nil)
{
filteredArray.append(textCell)
}
}
}
filteredTableData = filteredArray
self.tableView.reloadData()
}
}
What you can do is populate the filteredDataArray with no filtering at first, and then apply the filtering as it goes. You aren't setting filteredDataArray to anything until you start searching, so in the start of the search just set it to the full data set.
Bonus:
You can use tableViewDidSelectRowAtIndexPath to catch selection, and this should work even with the search table view.

Resources