Swift Search Bar with Multiple Data - ios

I have 5 labels that receive data from an array of SQL Query, in each cell on a tableView. I want to implement the search bar on it. Search bar should search through 1 specific label's data. it's working and filtering that specific label but when it's filtered, other 4 labels won't update and they are not changing at all. I can't figure it out. Since I have only 1 filtered data from search bar entry, I don't know how should I filter others.
Thanks
EDIT: Still can't figure it out about classes and adding data and i have an error of
"Value of type '[String]' has no member
'localizedCaseInsensitiveContains'"
on searchbar function
import UIKit
class TableViewController: UITableViewController, UISearchBarDelegate, UISearchDisplayDelegate {
var data:[MyData] = []
#IBOutlet weak var searchBar: UISearchBar!
var searchActive : Bool = false
var filtered:[MyData] = []
#IBAction func sqlExecute(_ sender: AnyObject) {
var client = SQLClient()
client.connect("sql_ip", username: "username", password: "password", database: "database") {
success in
if success {
client.execute("sql query") {
result in
for table in result as Any as! NSArray {
for row in table as! NSArray {
for column in row as! NSDictionary {
if column.key as! String == "data1" {
MyData.init(data1: "\(column.value)")
} else if column.key as! String == "data2" {
MyData.init(data2: column.value as! Double)
} else if column.key as! String == "data3" {
MyData.init(data3: "\(column.value)")
} else if column.key as! String == "data4" {
MyData.init(data4: "\(column.value)")
} else if column.key as! String == "data5" {
MyData.init(data5: "\(column.value)")
}
}
}
}
client.disconnect()
self.tableView.reloadData()
}
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
searchBar.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchActive) {
return filtered.count
} else {
return data.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "segue", for: indexPath)
if(searchActive) {
cell.label1.text = "\(filtered[indexPath.row])"
cell.label2.text = "\(filtered[indexPath.row])"
cell.label3.text = "\(filtered[indexPath.row])"
cell.label4.text = "\(filtered[indexPath.row])"
cell.label5.text = "\(filtered[indexPath.row])"
} else {
cell.label1.text = "\(data[indexPath.row])"
cell.label2.text = "\(data[indexPath.row])"
cell.label3.text = "\(data[indexPath.row])"
cell.label4.text = "\(data[indexPath.row])"
cell.label5.text = "\(data[indexPath.row])"
}
cell.backgroundColor = UIColor.clear
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searchActive = true;
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searchActive = false;
self.searchBar.endEditing(true)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchActive = false;
self.searchBar.endEditing(true)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchActive = false;
self.searchBar.endEditing(true)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filtered = data.filter { $0.data1.localizedCaseInsensitiveContains(searchText) }
if(filtered.count == 0){
searchActive = false;
} else {
searchActive = true;
}
self.tableView.reloadData()
}
This is the class file:
import Foundation
class MyData {
var data1 = [String]()
var data2 = [Double]()
var data3 = [String]()
var data4 = [String]()
var data5 = [String]()
init(data1: String) {
self.data1.append(data1)
}
init(data2: Double) {
self.data2.append(data2)
}
init(data3: String) {
self.data3.append(data3)
}
init(data4: String) {
self.data4.append(data4)
}
init(data5: String) {
self.data5.append(data5)
}
}

Your problem is you have five different Array instead of that you need to have single Array of type Dictionary or maybe custom Struct/class. It is batter if you use struct/class like this.
class MyData {
var data1: String!
var data2: Double!
var data3: String!
var data4: String!
var data5: String!
init(data1: String, data2: Double, data3: String, data4: String, data5: String) {
self.data1 = data1
self.data2 = data2
self.data3 = data3
self.data4 = data4
self.data5 = data5
}
}
Now instead of having five different array and one for filter create two Array of type [MyData] one of them is used for showing filterData and use that with tableView methods and filter it like this.
Create object of MyData using init method it and append its to the data Array then filter your in searchBar delegate like this.
filtered = data.filter { $0.data1.localizedCaseInsensitiveContains(searchText) }
The above filter will search in data array and return all objects thats property data1 contains searchText.
Your whole code would be like this
import UIKit
class TableViewController: UITableViewController, UISearchBarDelegate, UISearchDisplayDelegate {
var data:[MyData] = []
#IBOutlet weak var searchBar: UISearchBar!
var searchActive : Bool = false
var filtered:[MyData] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
searchBar.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchActive) {
return filtered.count
} else {
return data.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "segue", for: indexPath)
if(searchActive) {
cell.label1.text = filtered[indexPath.row].data1
cell.label2.text = filtered[indexPath.row].data2
cell.label3.text = filtered[indexPath.row].data3
cell.label4.text = "\(filtered[indexPath.row].data4) ₺"
cell.label5.text = filtered[indexPath.row].data5
} else {
cell.label1.text = data[indexPath.row].data1
cell.label2.text = data[indexPath.row].data2
cell.label3.text = data[indexPath.row].data3
cell.label4.text = "\(data[indexPath.row].data4) ₺"
cell.label5.text = data[indexPath.row].data5
}
cell.backgroundColor = UIColor.clear
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searchActive = true;
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searchActive = false;
self.searchBar.endEditing(true)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchActive = false;
self.searchBar.endEditing(true)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchActive = false;
self.searchBar.endEditing(true)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filtered = data.filter { $0.data1.localizedCaseInsensitiveContains(searchText) }
if(filtered.count == 0){
searchActive = false;
} else {
searchActive = true;
}
self.tableView.reloadData()
}
}
Edit: You need to use single array like this way.
for table in result as Any as! NSArray {
for row in table as! NSArray {
if let dic = row as? [String : Any] {
let data1 = dic["data1"] as! String
let data2 = dic["data2"] as! Double
let data3 = dic["data3"] as! String
let data4 = dic["data4"] as! String
let data5 = dic["data5"] as! String
let newData = MyData(data1: data1, data2: data2, data3: data3, data4: data4, data5: data5)
self.data.append(newData)
}
}
}
self.tableView.reloadData()

First you have to make a global array then in search bar delegates sort it .
extension HVUserLisitingViewController : UITableViewDelegate , UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filterUser.count
}
}
extension HVUserLisitingViewController : UISearchBarDelegate {
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
return true
}
func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
return true }
func searchBar(_ searchBar: UISearchBar, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return true
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { // called when text changes (including clear)
webserviceSearchBy(text: searchBar.text!)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
filterUser.removeAll()
filterUser = users
tableview.reloadData()
}
func webserviceSearchBy(text : String) {
if text.lowercased().count == 0 {
filterUser.removeAll()
filterUser = users
tableview.reloadData()
return
}
filterUser.removeAll()
filterUser = users.filter({ (user) -> Bool in
if user.username.lowercased().range(of:text.lowercased()) != nil {
return true
}
return false
})
tableview.reloadData()
}
}

Related

Indexpath row is zero after searching

I have been facing issue after searching of tableview. PDF files stored in Firebase are listed in alphabetic order in tableview and can be opened invidually in detailed view as well as after search an item , filtered item can be also viewed without any problem. However, after back to list and click same filtered pdf without refreshing the list, it gives me the first pdf of the list which is that the indexpath.row is zero.
For example, when I search and click on the item with indexpath.row number 3, I reach the relevant pdf file. But when I come back and click on the same filtered item, it brings up the first item of the whole list. Cant figure out how to handle it. Thank you.
import UIKit
import Firebase
import PDFKit
class IcsViewcontroller: UIViewController , UISearchBarDelegate {
var preImage : UIImage?
let cellSpacingHeight: CGFloat = 20
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var pdfListView: UITableView!
#IBOutlet weak var spinner: UIActivityIndicatorView!
var pdfList = [pdfClass]()
var searchall = [pdfClass]()
var searching = false
override func viewDidLoad() {
super.viewDidLoad()
pdfListView.delegate = self
pdfListView.dataSource = self
searchBar.delegate = self
self.pdfListView.isHidden = true
getPdf()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if searching {
let destination = segue.destination as! PdfKitViewController
let selectedIndexPath = pdfListView.indexPathForSelectedRow
destination.pdf = searchall[selectedIndexPath!.row]
} else {
let destination = segue.destination as! PdfKitViewController
let selectedIndexPath = pdfListView.indexPathForSelectedRow
destination.pdf = pdfList [selectedIndexPath!.row]
}
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
if searchBar.text == nil || searchBar.text == "" {
searching = false
} else {
searching = true
}
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searching = false
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searching = false
searchBar.text = ""
self.pdfListView.reloadData()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searching = false
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
searchall = pdfList
searching = false
pdfListView.reloadData()
} else {
searching = true
searchall = pdfList.filter({($0.pdf_name?.lowercased().prefix(searchText.count))! == searchText.lowercased() })
pdfListView.reloadData()
}
}
func getPdf () {
spinner.startAnimating()
let docRef = Storage.storage().reference().child("ICS_Documents")
docRef.listAll{ (result , error ) in
if let error = error {
print (error )
}
for item in result.items {
let storeageLocation = String( describing : item)
let gsReference = Storage.storage().reference(forURL: storeageLocation)
gsReference.downloadURL{ url , error in
//self.pdfList.removeAll()
if let error = error{
print(error)
} else {
let pdf_name = String( item.name)
let pdf_url = url?.absoluteString
let thumbnailSize = CGSize(width: 100, height: 100)
let thmbnail = self.generatePdfThumbnail(of: thumbnailSize, for: url!, atPage: 0)
let pdfall = pdfClass(pdf_name: pdf_name, pdf_url: pdf_url!, pdf_preview: thmbnail!)
self.pdfList.append(pdfall)
}
DispatchQueue.main.async {
self.pdfList = self.pdfList.sorted(by: { $0.pdf_name ?? "" < $1.pdf_name ?? ""})
self.pdfListView.reloadData()
self.spinner.stopAnimating()
self.pdfListView.isHidden = false
}
}
}
}
}
func generatePdfThumbnail(of thumbnailSize: CGSize , for documentUrl: URL, atPage pageIndex: Int) -> UIImage? {
let pdfDocument = PDFDocument(url: documentUrl)
let pdfDocumentPage = pdfDocument?.page(at: pageIndex)
return pdfDocumentPage?.thumbnail(of: thumbnailSize, for: PDFDisplayBox.trimBox)
}
}
extension IcsViewcontroller : UITableViewDelegate,UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching{
return searchall.count
}else {
return pdfList.count
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return cellSpacingHeight
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "pdfCell", for: indexPath) as! pdfCellTableViewCell
let varcell : pdfClass
if searching {
varcell = searchall [indexPath.row]
} else {
varcell = pdfList [indexPath.row]
}
cell.configure(name: varcell.pdf_name! , pdfthumbnail: varcell.pdf_preview!)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var indx : pdfClass
if searching{
indx = searchall[indexPath.row ]
print(indexPath.row )
}else {
indx = pdfList[indexPath.row]
}
performSegue(withIdentifier: "toPdfKit", sender: indx)
print(indexPath.row)
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let deleteAction = UITableViewRowAction(style: .default, title: "Delete", handler: { (action:UITableViewRowAction,indexPath:IndexPath)-> Void in
let storage = Storage.storage()
let childsRURL = self.pdfList[indexPath.row].pdf_url
let storageref = storage.reference(forURL: childsRURL!)
storageref.delete{ error in
if let error = error {
print(error.localizedDescription)
} else{
print("File deleted")
}
}
self.pdfListView.reloadData()
})
return [deleteAction]
}
}
This is the pdfClass
import Foundation
import UIKit
class pdfClass : NSObject {
var pdf_name : String?
var pdf_url : String?
var pdf_preview : UIImage?
override init(){
}
init (pdf_name :String , pdf_url : String, pdf_preview : UIImage ) {
self.pdf_name = pdf_name
self.pdf_url = pdf_url
self.pdf_preview = pdf_preview
}
}
I believe your problem is here, when you click on the cell, your searchBar editing is finished and you make the variable false, changing the list you are working on in the delegate.
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searching = false
}
For simplicity, I suggest keeping the original list just to load the results into a helper list that is used throughout the class, rather than working with two lists in each delegate.
Like this way:
import UIKit
class Shops {
private var _familiy_id: String?
private var _logo : String?
private var _shopname : String?
var familiy_id : String{
return _familiy_id!
}
var shopname : String{
return _shopname!
}
var Logo : String{
return _logo!
}
init(shopname : String , Logo : String , family_id : String) {
self._shopname = shopname
self._logo = Logo
self._familiy_id = family_id
}
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var shops : [Shops]! = []
var auxiliar : [Shops]!
override func viewDidLoad() {
super.viewDidLoad()
// 1 - load data to shops array
shops.append(Shops(shopname: "Brasil", Logo: "BR", family_id: "1"))
shops.append(Shops(shopname: "Brasolia", Logo: "BA", family_id: "2"))
shops.append(Shops(shopname: "Colombia", Logo: "CO", family_id: "3"))
shops.append(Shops(shopname: "Argentina", Logo: "AR", family_id: "4"))
// 2 - auxiliar receive the complete original array
auxiliar = shops
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return auxiliar.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
cell.textLabel?.text = auxiliar[indexPath.row].shopname
return cell
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
auxiliar = shops.filter { $0.shopname.range(of: searchText, options: .caseInsensitive, range: nil, locale: nil) != nil }
if searchText == "" {
// 3 if there is nothing to search, auxiliar receive the complete orihinal array
auxiliar = shops
}
tableView.reloadData()
}
}

Add a Searchbar on TableView but no response

Referring to shrikar's work - ://shrikar.com/swift-ios-tutorial-uisearchbar-and-uisearchbardelegate/, I try to build a searchbar in table view. The tableview works fine when no adding search text. The problem is no response and error message when add search text.
I wonder something wrong. Could anybody help us? Thanks.
import UIKit
import SDWebImage
class ArticleListViewController: UITableViewController, UISearchBarDelegate{
#IBOutlet weak var searchBar: UISearchBar!
var searchActive : Bool = false
var filtered = [Article]()
var articles = [Article](){
didSet{
DispatchQueue.main.async{
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
downLoadLatestArticles()
}
func downLoadLatestArticles(){
Article.downLoadItem { (articles, error) in
if let error = error {
print("fail \(error)")
return
}
if let articles = articles {
self.articles = articles
}
}
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchActive = true;
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
searchActive = false;
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if(searchText == " "){
searchActive = false
}else{
filtered = articles.filter ({ (article_f) -> Bool in
let tmp: String = article_f.name
let range = tmp.range(of:searchText, options: String.CompareOptions.caseInsensitive)
return range != nil
})
if(filtered.count == 0){
searchActive = false;
} else {
searchActive = true;
}
}
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchActive) {
return filtered.count
}
return articles.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ListTableCell", for: indexPath) as! ListTableCell
var article : Article
if(searchActive){
article = filtered[indexPath.row]
}
else{
article = articles[indexPath.row]
}
cell.nameLabel?.text = article.name
cell.locationLabel?.text = article.location
cell.photoView?.sd_setImage(with: article.image_URL)
return cell
}
}

Searching dynamic data issue from table view issue in swift

I have a search bar above my table view. The data in table view is coming from my service. I'm trying to apply search filter on the table view data. I have tried some code but it isn't working. My code for searchbar is this,
UIViewController,UISearchBarDelegate,UITextFieldDelegate,UITextViewDelegate,ShowsAlert
#IBOutlet weak var searchBar: UISearchBar!
var filteredData = [String]()
var isSearching = false
var dishNameArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
menuView.isHidden = true
reviewView.isHidden = true
infoView.isHidden = true
scrollView.isScrollEnabled = false
//TableView Delegates
menuTableView.delegate = self
menuTableView.dataSource = self
reviewTableView.delegate = self
reviewTableView.dataSource = self
reviewTableView.reloadData()
searchBar.delegate = self
searchBar.returnKeyType = UIReturnKeyType.done
segmentControl.tintColor = #colorLiteral(red: 0.9529120326, green: 0.3879342079, blue: 0.09117665142, alpha: 1)
searchBar.delegate = self
dishNameLbl.text = name
dishDescripLbl.text = resDesc
minOrderLbl.text = minOrder
deliveryLbl.text = deliveryTime
}
private func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
isSearching = true
}
private func searchBarTextDidEndEditing(searchBar: UISearchBar) {
isSearching = false
}
private func searchBarCancelButtonClicked(searchBar: UISearchBar) {
isSearching = false
}
private func searchBarSearchButtonClicked(searchBar: UISearchBar) {
isSearching = false
}
private func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
guard let searchText = searchBar.text else {
isSearching = false
return
}
filteredData = dishNameArray.filter({
return $0.lowercased().contains(searchText.lowercased())
})
isSearching = filteredData.count > 0
self.menuTableView.reloadData()
}
extension RestaurantMenuVC: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == menuTableView{
if isSearching{
return filteredData.count
}
return ResMenuService.instance.categoryModelInstance.count
}
else{
return AllReviewsService.instance.allReviewsModel.count
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if tableView == menuTableView{
return 57
}
else{
return 137
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == menuTableView{
let cell = menuTableView.dequeueReusableCell(withIdentifier: "menuCell", for: indexPath) as! RestaurantMenuTableViewCell
if isSearching{
cell.dishTitleLbl.text = filteredData[indexPath.row]
dishNameArray.append(cell.dishTitleLbl.text!)
}
// let cell = menuTableView.dequeueReusableCell(withIdentifier: "menuCell", for: indexPath) as! RestaurantMenuTableViewCell
cell.dishTitleLbl.text = ResMenuService.instance.categoryModelInstance[indexPath.row].categoryName
cell.cardView.layer.cornerRadius = 5
cell.selectionStyle = .none
return cell
}
else
{
let cell = reviewTableView.dequeueReusableCell(withIdentifier: "reviewCell", for: indexPath) as! AllReviewsTableViewCell
cell.nameLbl.text = AllReviewsService.instance.allReviewsModel[indexPath.row].name
cell.descriptionLbl.text = AllReviewsService.instance.allReviewsModel[indexPath.row].description
cell.timeLbl.text = AllReviewsService.instance.allReviewsModel[indexPath.row].time
cell.ratingView.rating = Double(AllReviewsService.instance.allReviewsModel[indexPath.row].rating)
cell.backgroundColor = UIColor.clear
cell.selectionStyle = .none
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView == menuTableView{
let minimumSpending = String(ResMenuService.instance.restaurntDetailModelInstance[indexPath.row].minimumSpending)
UserDefaults.standard.set(minimumSpending, forKey: "minimumSpending")
UserDefaults.standard.synchronize()
let categoryModel = ResMenuService.instance.categoryModelInstance
let subCategoryModel = ResMenuService.instance.categoryModelInstance[indexPath.row].subCategories
let AddonCategoryModel = ResMenuService.instance.categoryModelInstance[indexPath.row].subCategories[0].items[0].addonCategory
// if categoryId == subCategoryId{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "RestaurantMenuDetailVC") as! RestaurantMenuDetailVC
vc.categoryModel = categoryModel
vc.subCategoryModel = subCategoryModel
vc.AddonCategoryModel = AddonCategoryModel
self.navigationController?.pushViewController(vc, animated: true)
//}
}
else{
print("Hello")
}
}
}
But when i type something it does not filter the data. here is my model class,
struct RestaurantDetailModel {
public private(set) var restaurantId:String!
public private(set) var shopLat:String!
public private(set) var shopLng:String!
public private(set) var street:String!
public private(set) var town:String!
public private(set) var zipCode:String!
public private(set) var cellNo:String!
public private(set) var landLine:Int!
public private(set) var shopName:String!
public private(set) var deliveryTime:Int!
public private(set) var collectionTime:Int!
public private(set) var facebookLink:String!
public private(set) var twitterLink:String!
public private(set) var googleLink:String!
public private(set) var instagramLink:String!
public private(set) var pinterestLink:String!
public private(set) var address:String!
public private(set) var preorderPref:String!
public private(set) var orderStatus:Bool!
public private(set) var minimumSpending:Int!
public private(set) var restaurantTimings:[RestaurantTimingsModel]!
}
You dont set isSearching to true anywhere. So it is always false.
So the table never uses filteredData until you set isSearching = true
func updateSearchResults(for searchController: UISearchController) is a UISearchControllerDelegate method. As you are not using a UISearchController this will not be called in your case. You need to be using the UISearchBarDelegate functions.
Try the below changes. Reference used
in viewDidLoad add the following line:
searchBar.delegate = self
Add the following functions:
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
isSearching = true
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
isSearching = false
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
isSearching = false
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
isSearching = false
}
Change your updateSearchResults function to this:
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
guard let searchText = searchBar.text else {
isSearching = false
return
}
filteredData = dishNameArray.filter({
return $0.lowercased().contains(searchText.lowercased())
})
isSearching = filteredData.count > 0
self.menuTableView.reloadData()
}
You also need to make your ViewController conform to UISearchBarDelegate so add it, something like this:
class ViewController: UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate
This is much easier when using a UISearchController. See the example below.
Example using UISearchController:
I have just put this example together in a playground which shows how to do this with a UISearchController.
import UIKit
import PlaygroundSupport
class ViewController: UITableViewController {
let searchController = UISearchController(searchResultsController: nil)
var names = [
"John",
"Terry",
"Martin",
"Steven",
"Michael",
"Thomas",
"Jason",
"Matthew"
]
var filteredNames = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Search Example"
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search"
navigationItem.searchController = searchController
definesPresentationContext = true
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering() {
return filteredNames.count
} else {
return names.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell() // don't do this, i am for example.
var name: String
if isFiltering() {
name = filteredNames[indexPath.row]
} else {
name = names[indexPath.row]
}
cell.textLabel?.text = name
return cell
}
func searchBarIsEmpty() -> Bool {
// Returns true if the text is empty or nil
return searchController.searchBar.text?.isEmpty ?? true
}
func isFiltering() -> Bool {
return searchController.isActive && !searchBarIsEmpty()
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredNames = names.filter({( name : String) -> Bool in
return name.lowercased().contains(searchText.lowercased())
})
tableView.reloadData()
}
}
extension ViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
}
let vc = ViewController()
let nav = UINavigationController()
nav.viewControllers = [vc]
PlaygroundPage.current.liveView = nav
EDIT 2: Working SearchBar
import UIKit
import PlaygroundSupport
class ViewController: UITableViewController, UISearchBarDelegate {
var searchBar: UISearchBar!
var isFiltering = false
var names = [
"John",
"Terry",
"Martin",
"Steven",
"Michael",
"Thomas",
"Jason",
"Matthew"
]
var filteredNames = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Search Example"
searchBar = UISearchBar(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 50))
searchBar.delegate = self
tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 50))
tableView.tableHeaderView?.addSubview(searchBar)
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
isFiltering = false
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
isFiltering = false
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
isFiltering = true
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
isFiltering = false
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
guard let searchText = searchBar.text else {
isFiltering = false
return
}
filteredNames = names.filter({
return $0.lowercased().contains(searchText.lowercased())
})
isFiltering = filteredNames.count > 0
self.tableView.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering {
return filteredNames.count
} else {
return names.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell() // don't do this, i am for example.
var name: String
if isFiltering {
name = filteredNames[indexPath.row]
} else {
name = names[indexPath.row]
}
cell.textLabel?.text = name
return cell
}
}
let vc = ViewController()
PlaygroundPage.current.liveView = vc
EDIT 3: New information provided
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
guard let searchText = searchBar.text else {
isFiltering = false
return
}
filteredData = ResMenuService.instance.categoryModelInstance.filter({
return $0.categoryName.lowercased().contains(searchText.lowercased())
})
isFiltering = filteredData.count > 0
self.tableView.reloadData()
}
Try this
func updateSearchResults(for searchController: UISearchController) {
guard let searchtext = searchController.searchBar.text else {
return
}
filteredData.removeAll(keepingCapacity: false)
let filterPredicate = NSPredicate(format: "self contains[c] %#", argumentArray: [searchtext])
filteredData = dishNameArray.filter { filterPredicate.evaluate(with: $0) }
print(filteredData)
DispatchQueue.main.async {
self.menuTableView.reloadData()
}
}
You need to add function to identify searching state as below:
func isSearching() -> Bool {
return searchController.isActive && !searchBarIsEmpty()
}
func searchBarIsEmpty() -> Bool {
// Returns true if the text is empty or nil
return searchController.searchBar.text?.isEmpty ?? true
}

SearchBar with Custom Class

I am a beginner on iOS Development and I have an issue when I try to use the UISearchBar in a UITableView.
I do not know how to use the custom class in a table view which is filtering the data with a search bar. Please, would you fix the issue? Below is the custom class:
// Data.swift
// SearchBarWithTableView
class Food {
var FoodName:String = ""
var FoodQuantity:String = ""
var FoodGroup:String = ""
var FoodImage:String = ""
var FoodCarbs:Int = 0
var FoodCalories:Int = 0
init(foodName:String,foodGroup:String,foodQuantity:String,foodImage:String,foodCarbs:Int,foodCalories:Int) {
self.FoodName = foodName
self.FoodQuantity = foodQuantity
self.FoodGroup = foodGroup
self.FoodImage = foodImage
self.FoodCarbs = foodCarbs
self.FoodCalories = foodCalories
}}
ViewController.swift
import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate{
#IBOutlet weak var SearchBarTableView:UITableView!
#IBOutlet weak var SearchBar:UISearchBar!
var searchActive:Bool=false
var filteredData=[Food]()
let allData:[Food]=[Food(foodName: "Rice", foodGroup: "Beans", foodQuantity: "5 scope",foodImage:"1S.jpg", foodCarbs:15,foodCalories: 80),
Food(foodName: "Apple", foodGroup: "Frutie", foodQuantity: "One",foodImage:"S-2.jpg", foodCarbs: 15, foodCalories: 80),
Food(foodName: "ban 1",foodGroup: "Beans",foodQuantity:"One",foodImage:"3S.jpg",foodCarbs: 15,foodCalories: 80),
Food(foodName: "ban 2", foodGroup: "Beans", foodQuantity: "half pieace",foodImage:"4-S.jpg", foodCarbs: 25, foodCalories: 140)]
override func viewDidLoad() {
super.viewDidLoad()
SearchBarTableView.delegate=self
SearchBarTableView.dataSource=self
SearchBar.delegate=self
filteredData=allData
print(filteredData.count)
print("-----------")
print(allData.count)
SearchBar.returnKeyType=UIReturnKeyType.done
definesPresentationContext=true
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell=tableView.dequeueReusableCell(withIdentifier: "cell")
var Text:String
if searchActive {
Text=filteredData[indexPath.row].FoodName
}else{
Text=allData[indexPath.row].FoodName
}
cell?.textLabel?.text=filteredData[indexPath.row].FoodName
return cell!
}
func searchBar(_ searchBar: UISearchBar, textDidChange `searchText: String) {`
if SearchBar.text == nil || SearchBar.text == "" {
searchActive=false
view.endEditing(true)
SearchBarTableView.reloadData()
}else{
searchActive=true
/* here is the problem */
// filteredData=allData.filter({$0 === searchBar.text!})
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchActive {
return filteredData.count
}
return allData.count
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier=="Go"{
if let indexPath=SearchBarTableView.indexPathForSelectedRow{
let destinationVC=segue.destination as! DetailsViewController
destinationVC.namE=allData[indexPath.row].FoodName
destinationVC.quntitY=allData[indexPath.row].FoodQuantity
destinationVC.calorieS=allData[indexPath.row].FoodCalories
destinationVC.carbS=allData[indexPath.row].FoodCarbs
}
}
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange `searchText: String) {`
if SearchBar.text == nil || SearchBar.text == "" {
searchActive=false
view.endEditing(true)
SearchBarTableView.reloadData()
}else{
searchActive=true
filteredData=allData.filter({$0.FoodName === searchBar.text!})
SearchBarTableView.reloadData()
}
}
func updateSearchResults(for searchController: UISearchController) {
if let searchText = searchController.searchBar.text, !searchText.isEmpty {
self.filteredData = self.allData.filter{
var found = false
for str in ($0 as! NSDictionary) {
if(str.key as! String == "foodName")
{
var strin = str.value as! String;
strin = strin.lowercased();
let search = searchText.lowercased()
found = strin.contains(search);
}
}
return found
}
}
else {
self.Array = response as! [Any]
}
tableView.reloadData()
}
Use above method.

search bar in ios swift

I want to use search bar in my app.I am trying to use it but exceptions are coming . I have got an array of dictionary called member [[String:Anyobject]] and from this i have taken out the name and stored into an array data of type string and it is not working.
Here is my code :
import UIKit
class hcbaViewController: UIViewController,UITableViewDataSource,UITableViewDelegate,UISearchBarDelegate {
#IBOutlet var searchbar: UISearchBar!
#IBOutlet var tableview: UITableView!
var member = [[String:AnyObject]]()
var members = [String:AnyObject]()
var searchActive = true
var filtered:[String] = []
var data: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
print(data)
print("________-----------________----------")
print(member)
// Do any additional setup after loading the view.
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searchActive = true
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searchActive = false
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchActive = false
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchActive = false
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filtered = data.filter({ (text) -> Bool in
let tmp:NSString = text as NSString
let range = tmp.range(of: searchText, options: NSString.CompareOptions.caseInsensitive)
return range.location != NSNotFound
})
if (filtered.count == 0){
searchActive = false
}
else{
searchActive = true
}
self.tableview.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "MemberDirectory"
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return member.count
if(searchActive){
return filtered.count
}
else{
return data.count
}
// return member.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath)
var display = member[indexPath.row]
cell.textLabel?.text = display["Name"] as! String?
cell.detailTextLabel?.text = display["email"] as? String
let n = display["Name"] as! String
data.append(n)
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! hcbadetailViewController
vc.kk = members
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
members = member[indexPath.row]
self.performSegue(withIdentifier: "bye", sender: nil)
}
You can try this...
class SearchNew: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, GADInterstitialDelegate{
var SearchBarValue:String!
var searchActive : Bool = false
var data : NSMutableArray!
var filtered:NSMutableArray!
#IBOutlet var searchBar: UISearchBar!
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar.showsCancelButton = false
tableView.tableFooterView = UIView(frame: CGRectZero)
/* Setup delegates */
tableView.delegate = self
tableView.dataSource = self
searchBar.delegate = self
self.searchBar.delegate = self
data = []
filtered = []
self.getData()
} //-----viewDidLoad closed------
func getData()
{
//insert member data within data array
data.addObject(member)
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searchActive = true
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searchActive = false
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchActive = false;
searchBar.text = nil
searchBar.resignFirstResponder()
tableView.resignFirstResponder()
self.searchBar.showsCancelButton = false
tableView.reloadData()
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
searchActive = false
}
func searchBarShouldEndEditing(searchBar: UISearchBar) -> Bool {
return true
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
self.searchActive = true;
self.searchBar.showsCancelButton = true
filtered.removeAllObjects()
dispatch_to_background_queue
{
for xdata in self.data
{
let nameRange: NSRange = xdata.rangeOfString(searchText, options: [NSStringCompareOptions.CaseInsensitiveSearch ,NSStringCompareOptions.AnchoredSearch ])
if nameRange.location != NSNotFound{
self.filtered.addObject(xdata)
}
}//end of for
self.dispatch_to_main_queue {
/* some code to be executed on the main queue */
self.tableView.reloadData()
} //end of dispatch
}
}
func dispatch_to_main_queue(block: dispatch_block_t?) {
dispatch_async(dispatch_get_main_queue(), block!)
}
func dispatch_to_background_queue(block: dispatch_block_t?) {
let q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(q, block!)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchActive) {
return filtered.count
}else{
return data.count
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showDetailView") {
if let destination=segue.destinationViewController as? DetailViewController{
let path=tableView.indexPathForSelectedRow
let cell=tableView.cellForRowAtIndexPath(path!)
destination.passedValue=(cell?.textLabel?.text)
}
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
searchBar.resignFirstResponder()
searchBar.endEditing(true)
self.view.endEditing(true)
self.searchBar.showsCancelButton = false
self.searchBar.text=""
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! as UITableViewCell;
if(searchActive){
cell.textLabel?.text = filtered[indexPath.row] as! NSString as String
} else {
cell.textLabel?.text = data[indexPath.row]as! NSString as String
}
return cell;
}
}
Hope it helps you.
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate {
#IBOutlet var tblview: UITableView!
#IBOutlet var searchview: UISearchBar!
var data:[String] = ["Dev","Hiren","Bhagyashree","Himanshu","Manisha","Trupti","Prashant","Kishor","Jignesh","Rushi"]
var filterdata:[String]!
override func viewDidLoad() {
super.viewDidLoad()
tblview.dataSource = self
searchview.delegate = self
filterdata = data
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filterdata.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tblview.dequeueReusableCell(withIdentifier: "cell", for: indexPath)as!TableViewCell1
if filterdata.count != 0
{
cell.textview.text = filterdata[indexPath.row]
}
else{
cell.textview.text = data[indexPath.row]
}
return cell
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
// filterdata = searchText.isEmpty ? data : data.filter {(item : String) -> Bool in
filterdata = searchText.isEmpty ? data : data.filter { $0.contains(searchText) }
//return item.range(of: searchText, options: .caseInsensitive, range: nil, locale: nil) != nil
tblview.reloadData()
}
let searchController = UISearchController(searchResultsController: nil)
navigationItem.hidesSearchBarWhenScrolling = true
navigationItem.searchController = searchController
Replace this method with your TableView's method
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchActive){
return filtered.count
}
else{
return data.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath)
var display = searchActive == true ? filtered[indexPath.row] :
data[indexPath.row]
cell.textLabel?.text = display["Name"] as! String?
cell.detailTextLabel?.text = display["email"] as? String
let n = display["Name"] as! String
data.append(n)
return cell
}

Resources