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)
}
}
Related
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()
}
}
I want to search for some data in the table view using the search bar, but when I try to find data in my model, I'm not able to search that data.I made a expand table view cell and created a search bar for searching data, but still I can't search the data in the model. How can I achieve that?
here is my code:
import UIKit
class FAQViewController: UIViewController, UITableViewDataSource {
var dataFaq = [modelFAQ]()
let items = [
modelFAQ(name: "1. search box", description: "The design led users to instinctively search for their question first before clicking the FAQs"),
modelFAQ(name: "2.list of FAQs ", description: "Customers clicked around on the FAQs first."),
modelFAQ(name: "3. customers", description: "top issues first and then use the search for the questions instead of browsing and yielding more relevant results")
]
#IBOutlet fileprivate weak var tableView: UITableView!
#IBOutlet weak var searchDataBar: UISearchBar!
fileprivate var indexPaths: Set<IndexPath> = []
var cellIdentifier = "dataSourceFAQ"
var searchData = [String]()
var searching = false
override var preferredStatusBarStyle: UIStatusBarStyle{
return .lightContent
}
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
searchDataBar.delegate = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return searchData.count
}else {
return items.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! FAQTableViewCell
if searching{
cell.titleLabel.text = searchData[indexPath.row]
}else{
cell.titleLabel.text = items[indexPath.row].name
}
let nameDetail = self[indexPath].name as? String
let description = self[indexPath].description
cell.update(name: nameDetail ?? "0", description: description)
cell.state = cellIsExpanded(at: indexPath) ? .expanded : .collapsed
return cell
}
override func viewWillAppear(_ animated: Bool) {
tabBarController?.tabBar.isHidden = true
}
private func setupTableView(){
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 200.0
tableView.separatorStyle = .none
}
}
extension FAQViewController: UITableViewDelegate{
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as!FAQTableViewCell
cell.state = .expanded
self.addExpandedIndexPath(indexPath)
tableView.beginUpdates()
tableView.endUpdates()
print("1")
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! FAQTableViewCell
cell.state = .collapsed
self.removeExpandedIndexPath(indexPath)
tableView.beginUpdates()
tableView.endUpdates()
print("2")
}
}
extension FAQViewController {
func cellIsExpanded(at indexPath: IndexPath) -> Bool {
return indexPaths.contains(indexPath)
}
func addExpandedIndexPath(_ indexPath: IndexPath) {
indexPaths.insert(indexPath)
}
func removeExpandedIndexPath(_ indexPath: IndexPath) {
indexPaths.remove(indexPath)
}
}
extension FAQViewController {
subscript(indexPath: IndexPath) -> modelFAQ {
return items[indexPath.row]
}
}
extension FAQViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchData = searchText.isEmpty ? items: items.filter{ $0.name(searchText)}
searching = true
tableView.reloadData()
}
}
here is my table view cell
import UIKit
class FAQTableViewCell: UITableViewCell {
enum cellState{
case collapsed
case expanded
var carretImage: UIImage {
switch self {
case .collapsed:
return UIImage(named: "ic_arrow_down")!
case .expanded:
return UIImage(named: "ic_arrow_up")!
}
}
}
#IBOutlet private weak var stackView: UIStackView!
#IBOutlet private weak var containerView: UIView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet private weak var carret: UIImageView!
#IBOutlet private weak var descriptionLabel: UILabel!
private let expandedViewIndex: Int = 1
var state: cellState = .expanded {
didSet{
toggle()
}
}
override func awakeFromNib() {
selectionStyle = .none
containerView.layer.cornerRadius = 5.0
}
private func toggle(){
stackView.arrangedSubviews[expandedViewIndex].isHidden = stateIsCollapsed()
carret.image = state.carretImage
}
private func stateIsCollapsed() -> Bool{
return state == .collapsed
}
func update(name: String, description: String){
titleLabel.text = name
descriptionLabel.text = description
}
}
here is my model
struct modelFAQ {
var name: String
var description: String
}
Two issues:
You have to declare searchData as the same type as the main data.
By the way according to the naming convention struct and class names start with a capital letter
var searchData = [ModelFAQ]()
The search filter closure is wrong. Write
searchData = searchText.isEmpty ? items : items.filter{ $0.name.contains(searchText)}
or if you want to search case insensitive
searchData = searchText.isEmpty ? items : items.filter{ $0.name.range(of: searchText, options: .caseInsensitive) != nil }
And you have to change in cellForRowAt
cell.titleLabel.text = searchData[indexPath.row].name
NOTE: Always start class/struct name with CAPITAL LETTER
Wrong: modelFAQ
correct: ModelFAQ
You have made one mistake, you have to declare your searchData Array like below
var searchData = [ModelFAQ]()
and while in datasource method you have to get name from the ModelFAQ object and assign it to your label.
Hope it will help you.
I have a textField, which when touch displays a tableView with some rows.
I'm trying to do this: when a user selects one of the rows, the value of row is placed in the textField and the tableView is closed.
The first part works well for me. The user touch on one row and the textField shows the value of that row. But if I want to close the tableview, I have to press twice on the row.
This is my code:
class Redactar_mensaje: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {
var values = ["123 Main Street", "789 King Street", "456 Queen Street", "99 Apple Street", "red", "orange", "yellow", "green", "blue", "purple", "owaldo", "ostras", "Apple", "Pineapple", "Orange", "Adidas"]
#IBOutlet weak var campo_para: UITextField!
#IBOutlet weak var tableView: UITableView!
var originalCountriesList:[String] = Array()
override func viewDidLoad() {
super.viewDidLoad()
tableView.isHidden = true
for country in values {
originalCountriesList.append(country)
}
campo_para.delegate = self
tableView.delegate = self
tableView.dataSource = self
campo_para.addTarget(self, action: #selector(textFieldActive), for: UIControlEvents.touchDown)
campo_para.addTarget(self, action: #selector(searchRecords(_ :)), for: .editingChanged)
}
#objc func searchRecords(_ textField: UITextField) {
self.values.removeAll()
if textField.text?.count != 0 {
for country in originalCountriesList {
if let countryToSearch = textField.text{
let range = country.lowercased().range(of: countryToSearch, options: .caseInsensitive, range: nil, locale: nil)
if range != nil {
self.values.append(country)
}
}
}
} else {
for country in originalCountriesList {
values.append(country)
}
}
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return values.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cellx")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "cellx")
}
cell?.textLabel?.text = values[indexPath.row]
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
campo_para.text = values[indexPath.row]
tableView.isHidden = true //I need press twice for this. I want press only one
}
func textFieldActive() {
tableView.isHidden = false
}
}
Ideally, the user touches the textField, displays the tableView, chooses one of the values, and it close automatically the tableView. But this last one does not work well.
Any advice?
Details
xCode 8.3, Swift 3.1
Example to Detect Double tap and Single tap on TableViewCell
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.tableFooterView = UIView()
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
cell.label.text = "\(indexPath)"
cell.delegate = self
return cell
}
}
extension ViewController:TableViewCellDelegate {
func tableViewCell(singleTapActionDelegatedFrom cell: TableViewCell) {
let indexPath = tableView.indexPath(for: cell)
print("singleTap \(String(describing: indexPath)) ")
}
func tableViewCell(doubleTapActionDelegatedFrom cell: TableViewCell) {
let indexPath = tableView.indexPath(for: cell)
print("doubleTap \(String(describing: indexPath)) ")
//You can hide your textfield here
}
}
TableViewCell.swift
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var label: UILabel!
private var tapCounter = 0
var delegate: TableViewCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction))
addGestureRecognizer(tap)
}
func tapAction() {
if tapCounter == 0 {
DispatchQueue.global(qos: .background).async {
usleep(250000)
if self.tapCounter > 1 {
self.doubleTapAction()
} else {
self.singleTapAction()
}
self.tapCounter = 0
}
}
tapCounter += 1
}
func singleTapAction() {
delegate?.tableViewCell(singleTapActionDelegatedFrom: self)
}
func doubleTapAction() {
delegate?.tableViewCell(doubleTapActionDelegatedFrom: self)
}
}
TableViewCellDelegate.swift
import UIKit
protocol TableViewCellDelegate {
func tableViewCell(singleTapActionDelegatedFrom cell: TableViewCell)
func tableViewCell(doubleTapActionDelegatedFrom cell: TableViewCell)
}
Result
Here I put my solution, in case someone else would happen something similar.
Just change the order of the lines and add one more line. First it makes it invisible and then puts the result in the textField and, magically, it worked!
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.isHidden = true
campo_para.text = NombreUsuario[indexPath.row]
campo_asunto.becomeFirstResponder()
}
Thanks!
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()
}