Here is my single line Search functionality code which is not filtering results properly. Can anyone let me know what I have to change in the code to display the array elements I am searching with first three characters of text.
For the first character I entered it is showing results. But entering second and third elements it is not showing any results
Search Functionality Logic:
searchFruit = data.filter{$0.range(of: textSearched, options: [.caseInsensitive, .anchored]) != nil}
try this:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var data = ["apple","bananna","dragon fruit", "mango","pineapple"]
var searchFruit = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.delegate = self
tableView.dataSource = self
searchBar.delegate = self
searchFruit = data
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchFruit.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = searchFruit[indexPath.row]
return cell!
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchFruit = (searchText.isEmpty) ? self.data : self.data.filter({ (searchValue) -> Bool in
return searchValue.range(of: searchText, options: .caseInsensitive) != nil
})
tableView.reloadData()
}
}
Related
I am trying to add a UISearchBar to a UITableviewController that gets data from a db call. I added uisearchbar above the tableview in storyboard and made the outlet added delegate in class declaration and viewdidload. Code seems right but I am getting no reaction when typing in the searchClients function. Not sure what I am missing. No errors showing in console.
import UIKit
class TableViewController: UITableViewController, UISearchBarDelegate {
var CompanyID = ""
var CompanyName = ""
var ClientList = [Client]()
var filteredArray = [Client]()
let URL_SERVICE = "https://fetch.php"
#IBOutlet var searchClients: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
searchClients.delegate = self
filteredArray = ClientList
// omitted call to get data, as it loads fine initially
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Client", for: indexPath)
let client = filteredArray[indexPath.row]
let title = client.Name
cell.textLabel?.text = title
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.CompanyID = filteredArray[indexPath.row].CompanyID
self.CompanyName = filteredArray[indexPath.row].Name
}
func searchClients(_ searchBar: UISearchBar, textDidChange searchText: String) {
//let text: String = self.searchClients.text ?? ""
print("search for \(searchText)")
self.filteredArray = []
if (searchText == "") {
self.filteredArray = self.ClientList
} else {
for item in self.ClientList {
if (item.Name.lowercased().contains(searchText.lowercased())) {
self.filteredArray.append(item)
}
}
}
print(filteredArray)
self.tableView.reloadData()
}
}
What am I missing here that keeps searchBar function from firing?
You are not implementing any method from the UISearchBarDelegate since your searchClients function is not correctly named. You will need to rename it to searchBar to actually implement the function from the delegate protocol.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
Few Nitpicks
In Swift we are naming properties by starting with a lowercase letter
var companyID = ""
var companyName = ""
var clientList = [Client]()
var filteredArray = [Client]()
let urlService = "https://fetch.php"
You can skip every self. you are adding as a prefix when referencing to a property or function within the scope of the declaring type.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
companyID = filteredArray[indexPath.row].companyID
companyName = filteredArray[indexPath.row].name
}
Its much cleaner when protocol conformances are implemented using extensions so the code is nicely separated and they do not mix with each other.
extension TableViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
//let text: String = self.searchClients.text ?? ""
print("search for \(searchText)")
filteredArray = []
if searchText == "" {
filteredArray = clientList
} else {
filteredArray = clientList.filter { item in
item.name.lowercased().contains(searchText.lowercased())
}
}
print(filteredArray)
tableView.reloadData()
}
}
Im building an app and I have a search bar with table view.
But I don't how when users tap the search, go to the data at different View Controller
Someone can help me pls ?
My code almost like that
#IBOutlet weak var textSearchBar: UITextField!
#IBOutlet weak var tableSearchResult: UITableView!
var fruitsArray:[String] = Array()
var searchedArray:[String] = Array()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
fruitsArray.append("Apple")
fruitsArray.append("Orange")
fruitsArray.append("Litch")
fruitsArray.append("Pineapple")
for str in fruitsArray {
searchedArray.append(str)
}
tableSearchResult.dataSource = self
textSearchBar.delegate = self
}
// Mark:- UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchedArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
}
cell?.textLabel?.text = searchedArray [indexPath.row]
return cell!
}
// MARK: - UITextFieldDelegate
func textFieldShouldClear(_ textField: UITextField) -> Bool {
textSearchBar.resignFirstResponder()
textSearchBar.text = ""
self.searchedArray.removeAll()
for str in fruitsArray {
searchedArray.append(str)
}
tableSearchResult.reloadData()
return true
}
Thank you Very Much
Try this:
extension ViewController: UISearchBarDelegate {
func searchBar(
_ searchBar: UISearchBar,
textDidChange searchText: String
) {
// Here instructions for when searchBarText change
}
func searchBarCancelButtonClicked(
_ searchBar: UISearchBar
) {
self.searchBar.endEditing(true)
}
}
There is table view to show phone contact and a search Bar in view controller . I implemented the code for the search-bar to filter givenName array it works just fine but when I click on the cell that I searched it doesn’t display the right information it displays only the information of the first row( exactly first index). The question it is how i can solve this problem ?
Data Model :
struct ContactStruct {
let identifier : String
let thumbnailImageData : UIImage
let givenName : String
let familyName : String
let phoneNumbers : String
let emailAddresses : String
}
Main View controller :
class NewContactViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var contactsData = [ContactStruct]()
var searchedContact = [String]()
var searching = false
#IBOutlet weak var tblMain: UITableView!
#IBOutlet weak var contactSearchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let story = UIStoryboard(name: "Main", bundle: nil)
let vc = story.instantiateViewController(withIdentifier: "InsertContactViewController") as! InsertContactViewController
vc.strEditFitstName = contactsData[indexPath.row].givenName
vc.stridentifier = contactsData[indexPath.row].identifier
self.navigationController?.pushViewController(vc, animated: true)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return searchedContact.count
} else {
return contactsData.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "testCell") as! NewContactCell
let contactToDisplay = contactsData[indexPath.row]
contactToDisplay.familyName
cell.lblLName.text = contactToDisplay.givenName
return cell
}
search bar methods:
extension NewContactViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
let givenNames = contactsData.map { $0.givenName }
searchedContact = givenNames.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()})
searching = true
tblMain.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searching = false
searchBar.text = ""
tblMain.reloadData()
}
}
Follow just below code as an example :
let countriesList = ["African Union",
"Andorra",
"Armenia",
"Austria",
"Bahamas",
"Barbados",
"Belarus",
"Belgium",
]
#IBOutlet var tblPlace: UITableView!
#IBOutlet var searchPlace: UISearchBar!
var selctedCountries:[String] = []
var filteredData: [String]!
override func viewDidLoad() {
super.viewDidLoad()
searchPlace.delegate = self
filteredData = countriesList
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ApplyFilterTableViewCell", for: indexPath) as! ApplyFilterTableViewCell
cell.lblName = filteredData[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let story = UIStoryboard(name: "Main", bundle: nil)
let vc = story.instantiateViewController(withIdentifier: "InsertContactViewController") as! InsertContactViewController
vc.strEditFitstName = filteredData[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredData = searchText.isEmpty ? countriesList : countriesList.filter({(dataString: String) -> Bool in
// If dataItem matches the searchText, return true to include it
return dataString.range(of: searchText, options: .caseInsensitive) != nil
})
tblPlace.reloadData()
}
I am having a weird issue where for some reason my UITableView is not being reloading after performing a search. The console prints out the correctly filtered data, but the table simply doesn't change. I have never encountered this issue, so I first attempted the solutions which naturally came to mind:
Tried tableView.reloadData() in the Main Queue
Quit Xcode, clean build, reinstall
Cleared out the derived data dir
I have found several similar issue in SO, but all of the solutions I've seen are things I've tried, mainly reloading tableview in main queue.
Hoping maybe I just simply have an issue in my code or something I'm missing.
I am running Xcode 8.3.3
import UIKit
class CategoriesViewController: UIViewController {
var isFiltering = false
var location = Location()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var categoriesSearchResults = [Category]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.allowsSelection = true
tableView.keyboardDismissMode = .onDrag
let nib = UINib(nibName: "CategoryTableViewCell", bundle: nil)
self.tableView.register(nib, forCellReuseIdentifier:"CategoryTableViewCell");
searchBar.returnKeyType = UIReturnKeyType.done
searchBar.autocapitalizationType = .none
searchBar.delegate = self
}
extension CategoriesViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("HI")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering {
return self.categoriesSearchResults.count
}
return self.location.categories.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if let cell = self.tableView.dequeueReusableCell(withIdentifier: "CategoryTableViewCell", for: indexPath) as? CategoryTableViewCell {
var category: Category
if isFiltering {
category = self.categoriesSearchResults[indexPath.row]
} else {
category = self.location.categories[indexPath.row]
}
cell.name.text = category.name
cell.status.textColor = UIColor.lightGray
cell.status.text = "Not Verified"
}
return cell
}
}
extension CategoriesViewController : UISearchBarDelegate {
func searchBarIsEmpty() -> Bool{
return self.searchBar.text?.isEmpty ?? true
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.isFiltering = true
self.categoriesSearchResults.removeAll()
tableView.reloadData()
self.view.endEditing(true)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBarIsEmpty() {
self.view.endEditing(true)
self.isFiltering = false
} else {
self.isFiltering = true
self.categoriesSearchResults = self.location.categories.filter({ (category: Category) -> Bool in
return category.name.lowercased().contains(searchText.lowercased())
})
}
tableView.reloadData()
}
}
and my custom table view cell:
import UIKit
class CategoryTableViewCell: UITableViewCell {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var status: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func prepareForReuse() {
super.prepareForReuse()
self.name.text = ""
self.status.text = ""
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Thank you in advance.
EDIT: Might also be worth mentioning, when I am actively searching, the function tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) is not called??
The scope of if let nests in its scope. In your code you are always returning let cell = UITableViewCell(). Try returning it inside the if let :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if let cell = self.tableView.dequeueReusableCell(withIdentifier: "CategoryTableViewCell", for: indexPath) as? CategoryTableViewCell {
var category: Category
if isFiltering {
category = self.categoriesSearchResults[indexPath.row]
} else {
category = self.location.categories[indexPath.row]
}
cell.name.text = category.name
cell.status.textColor = UIColor.lightGray
cell.status.text = "Not Verified"
/// RETURN CELL HERE
return cell
}
return cell
}
I am interested in using a tableView to list possible addresses based on inputs in the search bar. After selecting the cell that contains the address desired, the search bar text consists of the address, however I want the possible addresses (cells) to disappear. Does self.searchResultsTableView.reloadData() in didSelectRowAt clear all the cells or is there another command? I am not certain how to clear the cells after selecting the appropriate address without iterating and having the suggestion introduce more cells.
import UIKit
import MapKit
class SearchViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
var searchCompleter = MKLocalSearchCompleter()
var searchResults = [MKLocalSearchCompletion]()
var searchSource: [String]?
#IBOutlet weak var searchResultsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
searchCompleter.delegate = self
searchBar.delegate = self
}
}
extension SearchViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchCompleter.queryFragment = searchText
}
}
extension SearchViewController: MKLocalSearchCompleterDelegate {
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
searchResults = completer.results
searchResultsTableView.reloadData()
}
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
// handle error
}
}
extension SearchViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResults.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let searchResult = searchResults[indexPath.row]
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
cell.textLabel?.text = searchResult.title
cell.detailTextLabel?.text = searchResult.subtitle
return cell
}
}
extension SearchViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let completion = searchResults[indexPath.row]
let searchRequest = MKLocalSearchRequest(completion: completion)
let search = MKLocalSearch(request: searchRequest)
search.start { (response, error) in
let coordinate = response?.mapItems[0].placemark.coordinate
print(String(describing: coordinate))
print(response?.mapItems)
self.searchBar.text = response?.mapItems[0].name
}
self.searchResultsTableView.reloadData()
}
}
If you want to clear your tableView then you need to make your datasource array empty and then reload the tableView.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let completion = searchResults[indexPath.row]
let searchRequest = MKLocalSearchRequest(completion: completion)
let search = MKLocalSearch(request: searchRequest)
search.start { (response, error) in
let coordinate = response?.mapItems[0].placemark.coordinate
print(String(describing: coordinate))
print(response?.mapItems)
self.searchBar.text = response?.mapItems[0].name
}
//Make empty your array ant then reload tableView
searchResults = []
self.searchResultsTableView.reloadData()
}