In tableViewCell I have userNameLbl with name, userClgLbl with number. I want to search and show data in tableView either name search or number search.
If user search name - based on name I can show data in tableView.
If user search number - based on number I can show data in tableView.
But how to work with both name and number for single search bar. Actually here my data is dynamic from server and number is not phone number.
UISearchBarDelegate added to my class
let searchBar = UISearchBar()
var filteredData: [Any]!
#IBOutlet weak var listTblView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
cell.userNameLbl.text = filteredData[indexPath.row] as? String
cell.userClgLbl.text = clg_uniq[indexPath.row] as? String
return cell
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
let strArr:[String] = clg_uniq as! [String]
filteredData = searchText.isEmpty ? clg_uniq : strArr.filter({(dataString: String) -> Bool in
// If dataItem matches the searchText, return true to include it
return dataString.range(of: searchText, options: .caseInsensitive) != nil
})
DispatchQueue.main.async {
self.listTblView.reloadData()
}
if searchText == "" {
DispatchQueue.main.async {
searchBar.resignFirstResponder()
}
}
}
//Added these lines after json parsing
self.filteredData = self.clg_uniq
self.listTblView.reloadData()
My example JSON data is
{"log" = (
{
Name = "Name1";
"clg_uniq" = 5c640e7b86e35;
},
{
Name = "Name2";
"clg_uniq" = <null>;
},
{
Name = <null>;
"clg_uniq" = 5c647af5d5c4d;
},
{
Name = "Name4";
"clg_uniq" = 5c647a0427253;
},
{
Name = <null>;
"clg_uniq" = <null>;
},
{
Name = "Name6";
"clg_uniq" = $cuniq";
},
)
}
Add following variables -
var logArray = [Dictionary<String, Any>]() // For all result
var searchedLogArray = [Dictionary<String, Any>]() // For filtered result
var searchActive = false // whenever user search anything
Replace UISearchBarDelegate -
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchActive = searchText.count > 0 ? true : false
let namePredicate = NSPredicate(format: "Name CONTAINS[c] %#", searchText)
let clgUniqPredicate = NSPredicate(format: "clg_uniq CONTAINS[c] %#", searchText)
let compoundPredicate = NSCompoundPredicate.init(orPredicateWithSubpredicates: [namePredicate, clgUniqPredicate])
searchedLogArray = logArray.filter({
return compoundPredicate.evaluate(with: $0)
})
listTblView.reloadData()
}
Replace UITableViewDataSource -
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchActive ? searchedLogArray.count : logArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
let logDict = searchActive ? searchedLogArray[indexPath.row] : logArray[indexPath.row]
// Name
if let name = log["Name"] as? String{
cell.userNameLbl.text = name
}else{
cell.userNameLbl.text = ""
}
// clg_uniq
if let clgUniq = log["clg_uniq"] as? String {
cell.userClgLbl.text = clgUniq
}else{
cell.userClgLbl.text = ""
}
return cell
}
I hope you are persing response as Dictionary<String, Any>
Let me know if you are still having any issue.
Related
I am trying to create a UITableView on my IOS app that displays a list of all the app's registered users from Firebase and includes a UISearchBar to search through them. I am very new to this and unfortunately this code is giving me the following error: "Value of type 'User' has no subscripts" on the line of code "let user = users[indexPath.row]"
There is an NSObject User class in another file that defines name and email as String?
These are the variables:
var searchActive : Bool!
var userList = [User]()
var filterUsers = [User]()
private var users = User()
private var hasFetched = false
Here is the search bar function I was using that is giving me the error:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filterUsers = userList.filter({ (text) -> Bool in
let temp: NSString = text.name! as NSString
let range = temp.range(of: searchText, options: NSString.CompareOptions.caseInsensitive)
return range.location != NSNotFound
})
if(filterUsers.count == 0){
searchActive = false;
} else {
searchActive = true;
}
//refreshTable()
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchActive) {
return filterUsers.count
}
return userList.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let user = userList[indexPath.row] as? [String : AnyObject] {
}
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")! as UITableViewCell;
let user = users[indexPath.row]
cell.textLabel?.text = user.name
cell.textLabel?.text = user.name
return cell;
}
}
}
Modify this:
let user = users[indexPath.row]
To this:
let user = searchActive ? filterUsers[indexPath.row] : userList[indexPath.row]
I am trying to show jsondata in to the tableView and search country from the searchBar but getting error in to the textDidChange function.
I want the user to enter three words into the searchBar then tableView will open and search data.
struct country : Decodable {
let name : String
let capital : String
let region : String
}
class ViewController: UIViewController,UISearchBarDelegate {
var isSearch : Bool = false
var countries = [country]()
var arrFilter:[String] = []
#IBOutlet weak var tableview: UITableView!
#IBOutlet weak var searchbar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
tableview.delegate = self
searchbar.delegate = self
let jsonurl = "https://restcountries.eu/rest/v2/all"
let url = URL(string: jsonurl)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do{
self.countries = try JSONDecoder().decode([country].self, from: data!)
}
catch{
print("Error")
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
}.resume()
}
shows error into this part.
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.characters.count == 0 {
isSearch = false;
self.tableview.reloadData()
} else {
arrFilter = countries.filter({ (text) -> Bool in
let tmp: NSString = text
let range = tmp.rangeOfString(searchText, options: NSStringCompareOptions.CaseInsensitiveSearch)
return range.location != NSNotFound
})
if(arrFilter.count == 0){
isSearch = false;
} else {
isSearch = true;
}
self.tableview.reloadData()
}
}
}
my table view part
extension ViewController : UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(isSearch){
return arrFilter.count
}
else{
return countries.coun
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if(isSearch){
cell.textLabel?.text = arrFilter[indexPath.row]
}else{
cell.textLabel?.text = countries[indexPath.row].name.capitalized
}
return cell
}
}
First of all do not use NSString in Swift and the Foundation rangeOfString API, use native String and native range(of.
Second of all never check for an empty string and for an empty array with .count == 0. There is isEmpty.
Third of all please name structs and classes with a starting capital letter. struct Country ....
The error occurs because you are filtering Country instances and actually you are looking for its name or its region.
This is a pure Swift version of your code
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
isSearch = false
} else {
arrFilter = countries.filter( $0.name.range(of: searchText, options: .caseInsensitive) != nil }
isSearch = !arrFilter.isEmpty
}
self.tableview.reloadData()
}
If you want to filter for name and region write
arrFilter = countries.filter( $0.name.range(of: searchText, options: .caseInsensitive) != nil
|| $0.region.range(of: searchText, options: .caseInsensitive) != nil }
With this syntax declare arrFilter
var arrFilter = [Country]()
and in cellForRow write
let dataArray = isSearch ? arrFilter : countries
cell.textLabel?.text = dataArray[indexPath.row].name.capitalized
You are getting country object of your array as a string so such an error occured..
Please do as below
var arrFilter:[country] = [country]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if(isSearch){
cell.textLabel?.text = arrFilter[indexPath.row].name.capitalized
}else{
cell.textLabel?.text = countries[indexPath.row].name.capitalized
}
return cell
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.characters.count == 0 {
isSearch = false;
self.tableview.reloadData()
} else {
arrFilter = countries.filter({ (country) -> Bool in
let tmp: NSString = NSString.init(string: country.name)
let range = tmp.range(of: searchText, options: NSString.CompareOptions.caseInsensitive)
return range.location != NSNotFound
})
if(arrFilter.count == 0){
isSearch = false;
} else {
isSearch = true;
}
self.tableview.reloadData()
}
}
First you can not assign a value type [Country] to [String].For example when assign a arrFilter at that time country.filter always return country type value not a string type.
use below code to helping you,
var countries = [country]()
var arrFilter:[country] = [country]()
inside the viewdidLoad
override func viewDidLoad() {
self.countries.append(country(name: "India", capital: "New Delhi", region: "Asia"))
self.countries.append(country(name: "Indonesia", capital: "Jakarta", region: "region"))
self.countries.append(country(name: "Australia", capital: "Canberra", region: "Austrialia"))
// Do any additional setup after loading the view.
}
And
self.arrFilter = self.countries.filter({ (country) -> Bool in
let temp : NSString = country.name as NSString //or you can use country.capital or country.region
let range = temp.range(of: "ind", options: .caseInsensitive)
print(range.location)
print(range.length)
print(temp)
return range.location != NSNotFound
})
Thanks
Hey guys i've searched for hours and still cant find a proper way to search though my data base. I have an array of contact objects that have a username and name property and I have a "add user" view controller where the GOAL is to loop through all the users in my data base , and when searching , it widdles down the users in a UITABLEVIEW this is what I have so far.
Cliff notes of code below:
I get all my user objects from my database and store them in an array of type [contact] called "results" (custom object) then i attempt to filter the results and store those into a new array called "filteredData" Contact has type "userName" (String) which I would like to filter results by
import UIKit
import Firebase
class SearchForUsersViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var results = [Contact]()
var filteredData = [Contact]()
var isSearching = false;
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
searchBar.delegate = self;
searchBar.returnKeyType = UIReturnKeyType.done
getUserList()
}
#IBAction func dismiss(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
func getUserList(){
//populates results
staticValuesForData.instance.dataBaseUserref.observe( .value) { (snapshot) in
if let userList = snapshot.children.allObjects as? [DataSnapshot]{
for user in userList{
let name = (user.childSnapshot(forPath: staticValuesForData.instance.fName).value as! String) + " "
+ (user.childSnapshot(forPath: staticValuesForData.instance.lname).value as! String)
let contact = Contact(name: name , uid: user.key,
pic: user.childSnapshot(forPath: staticValuesForData.instance.profileUrl).value as! String,
userName: user.childSnapshot(forPath: staticValuesForData.instance.userName).value as! String )
print(contact.name)
print("user" , user)
self.results.append(contact)
}
}
}
}
}
table view extension :
extension SearchForUsersViewController : UITableViewDataSource ,
UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearching{
return results.count
}
return 0;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell" , for: indexPath) as! AddedMeTableViewCell;
cell.profilePicture.loadImageUsingCacheWithUrlString(urlString: filteredData[indexPath.item].picUrl)
if isSearching{
cell.userName.text = filteredData[indexPath.item].userName!
}
else
{
cell.userName.text = results[indexPath.item].userName!
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80;
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
Search extension (where the issue is )
extension SearchForUsersViewController : UISearchBarDelegate{
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == "" || searchBar.text == nil{
view.endEditing(true)
isSearching = false;
tableView.reloadData()
}
else{
isSearching = true
ifSearchContains(word: searchBar.text!)
tableView.reloadData()
print(filteredData)
print(results)
print(searchBar.text)
}
}
func ifSearchContains(word : String)
{
for result in results{
if result.name.contains(word){
filteredData.append(result)
}else{
}
}
}
}
I have the search function above but it is not filtering , nor is the idea of it very efficient. this application is going to have thousands of users, can you please help me filter a search in an efficient way? Thank you so much
Here is the contact custom object just in case
import Foundation
class Contact : NSObject , Comparable{
let name : String!
let uid : String!
let picUrl : String!
let userName : String!
init(name : String , uid : String , pic : String , userName : String) {
self.name = name
self.uid = uid
self.picUrl = pic
self.userName = userName
}
static func ==(lhs: Contact, rhs: Contact) -> Bool {
return lhs.name == rhs.name
}
static func <(lhs: Contact, rhs: Contact) -> Bool {
return lhs.name < rhs.name
}
}
I don't really know how to explain thats why I'm putting this title. But i do hope you can understand. As of right now, my searchBar is able to return results BUT i have to enter the entire name (For example: I have to type "EcoBooth" in order for the searchBar to return EcoBooth results.).
How can i make it as though if i just enter "Eco", it will return EcoBooth based on just a few strings i type?
P.S: Do look at the 2 images attached for more info.
My TableView Controller codes
import UIKit
import FirebaseDatabase
var ref: DatabaseReference?
var databaseHandle: DatabaseHandle?
var postData = [String]()
var postData2 = [String]()
var currentpostDataArray = [String]()
var tableDataArray = [tableData]()
var searchArray = [tableData]()
var inSearchMode = false
class TableViewController: UITableViewController, UISearchBarDelegate {
#IBOutlet var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
setUpSearchBar()
ref = Database.database().reference() //set the firebase reference
// Retrieve the post and listen for changes
databaseHandle = ref?.child("Posts2").observe(.value, with: { (snapshot) in
// Code to execute when a child is added under "Posts"
postData.removeAll()
postData2.removeAll()
tableDataArray.removeAll()
for child in snapshot.children {
let snap = child as! DataSnapshot
let key = snap.key
let value = String(describing: snap.value!)
let rating = (value as NSString).integerValue
postData.append(key)
postData2.append(value)
tableDataArray.append(tableData(boothName: key, boothRating: rating))
currentpostDataArray = postData
}
self.tableView.reloadData()
})
}
private func setUpSearchBar() {
searchBar.delegate = self
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return currentpostDataArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = currentpostDataArray[indexPath.row]
cell.detailTextLabel?.text = postData2[indexPath.row] + " ♥"
cell.detailTextLabel?.textColor = UIColor.red;
return cell
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchArray.removeAll()
searchArray = tableDataArray
currentpostDataArray.removeAll()
postData2.removeAll()
if !searchText.isEmpty {
for data in searchArray {
let item = data.boothName
if (searchText.lowercased().range(of: item) != nil) || data.boothName.lowercased() == searchText.lowercased() || searchText.lowercased().contains(data.boothName.lowercased())
{
currentpostDataArray.append(data.boothName)
let value = String(describing: data.boothRating)
postData2.append(value)
}
}
}
if searchText.isEmpty {
loadDara()
}
self.tableView.reloadData()
}
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
switch selectedScope {
case 0:
currentpostDataArray.removeAll()
postData2.removeAll()
for data in tableDataArray {
currentpostDataArray.append(data.boothName)
let value = String(describing: data.boothRating)
postData2.append(value)
}
self.tableView.reloadData()
case 1:
currentpostDataArray.removeAll()
postData2.removeAll()
let sortedTableData = tableDataArray.sorted(by: { $0.boothRating > $1.boothRating })
for data in sortedTableData {
currentpostDataArray.append(data.boothName)
let value = String(describing: data.boothRating)
postData2.append(value)
}
self.tableView.reloadData()
default:
break
}
tableView.reloadData()
}
func loadDara() {
for data in tableDataArray {
currentpostDataArray.append(data.boothName)
let value = String(describing: data.boothRating)
postData2.append(value)
}
}
}
class tableData {
var boothName: String
var boothRating: Int
init(boothName: String, boothRating: Int) {
self.boothName = boothName
self.boothRating = boothRating
}
}
Replace
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchArray.removeAll()
searchArray = tableDataArray
currentpostDataArray.removeAll()
postData2.removeAll()
if !searchText.isEmpty {
for data in searchArray {
let item = data.boothName
if (searchText.lowercased().range(of: item) != nil) || data.boothName.lowercased() == searchText.lowercased() || searchText.lowercased().contains(data.boothName.lowercased())
{
currentpostDataArray.append(data.boothName)
let value = String(describing: data.boothRating)
postData2.append(value)
}
}
}
if searchText.isEmpty {
loadDara()
}
self.tableView.reloadData()
}
With
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchArray.removeAll()
searchArray = tableDataArray
currentpostDataArray.removeAll()
postData2.removeAll()
if !searchText.isEmpty {
for data in searchArray {
let item = data.boothName
if (item.lowercased().range(of: searchText.lowercased()) != nil)
{
currentpostDataArray.append(data.boothName)
let value = String(describing: data.boothRating)
postData2.append(value)
}
}
}
if searchText.isEmpty {
loadDara()
}
self.tableView.reloadData()
}
Use NSPredicate to search from array like this:
let searchPredicate = NSPredicate(format: "boothName CONTAINS[C] %#", searchText)
resultArr = (filteredArray as NSArray).filtered(using: searchPredicate)
You can prefer this link for more info about NSPredicate and also a swift code example : http://nshipster.com/nspredicate/
I'm trying to filter an array that I create based on objects (as Strings) that I queried. They show up great, now I just want to filter and remake the array so that I can filter out the information I need. I'm not sure why I'm getting "Result of call 'filter' Is unused" in Xcode. I looked around but I can't figure this one out.
import UIKit
class RegionStoreTableViewController: UITableViewController, UISearchBarDelegate {
var selectedRegionStore : String? = nil
var selectedRegionStoreIndex : Int? = nil
var dataArray = [String]()
var filteredArray = [String]()
var employeeType : String? = nil
var searchText = ""
#IBOutlet weak var regionStoreSearchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
let prefs = NSUserDefaults.standardUserDefaults()
if prefs.valueForKey("EmployeeType") != nil {
employeeType = prefs.valueForKey("EmployeeType") as! String
// Employee Type
// Retail
if employeeType == "Retail" {
self.navigationItem.title = "Store Selector"
let query = PFQuery(className: "Stores")
query.orderByAscending("rStoreNumber")
query.findObjectsInBackgroundWithBlock({ (store: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
for store in store! {
let theStore = store["storeName"] as! String
let storeNumber = store["rStoreNumber"] as! String
let storeString = storeNumber + " - " + theStore
print(theStore)
self.dataArray.append(storeString)
self.tableView.reloadData()
}
}
})
}
if employeeType == "Corporate" {
let query = PFQuery(className: "Regions")
query.orderByAscending("regionName")
query.findObjectsInBackgroundWithBlock { (region: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
for region in region! {
let theRegion = region["regionName"] as! String
print(theRegion)
self.dataArray.append(theRegion)
self.tableView.reloadData()
}
} else {
print(error)
}
}
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(dataArray.count)
return dataArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("RegionStoreCell", forIndexPath: indexPath)
if searchText.isEmpty {
cell.textLabel?.text = dataArray[indexPath.row]
}
if searchText != "" {
dataArray.filter() {nil != $0.containsString(searchText) }
}
if indexPath.row == selectedRegionStoreIndex {
cell.accessoryType = .Checkmark
} else {
cell.accessoryType = .None
}
return cell as UITableViewCell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
if let index = selectedRegionStoreIndex {
let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0))
cell?.accessoryType = .None
}
selectedRegionStoreIndex = indexPath.row
selectedRegionStore = dataArray[indexPath.row]
let cell = tableView.cellForRowAtIndexPath(indexPath)
cell?.accessoryType = .Checkmark
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SaveSelectedRegionStore" {
let cell = sender as! UITableViewCell
let indexPath = tableView.indexPathForCell(cell)
selectedRegionStoreIndex = indexPath?.row
if let index = selectedRegionStoreIndex {
selectedRegionStore = dataArray[index]
}
}
}
// MARK: Search Bar
// delegate in story board
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchBar.showsCancelButton = true
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
searchBar.showsCancelButton = false
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
// add minimum length of search
searchText = searchBar.text!
self.tableView.reloadData()
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
// clear out search box
searchBar.text = nil
// clear out search variable
searchText = ""
// reload the table
self.tableView.reloadData()
// hide keyboard
searchBar.resignFirstResponder()
}
}
Any suggestions?
I think you need to store your filtered array into another array.
let filterArray = dataArray.filter() {nil != $0.containsString(searchText)