Implementing a Search Text Box in Swift - ios

Would someone be able to help with the following in swift:
I have created a textfield. When I click on the textfield it displays a table with a list
I have created a func that searches through the array and any matches will be added to the filtered array but how then do i add this to my table
-Also is there a way that when i now select a new value from the table it populates into my textfield?
I'm basically trying to create a dropdown
This is my code
import UIKit
var names = ["frank", "john", "harry", "mike", "jones"]
var filtered:[String] = []
class ViewController: UIViewController {
var searchActive : Bool = false
#IBOutlet weak var searchText: UITextField!
#IBOutlet weak var personTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
personTable.alpha = 0
// 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.
}
#IBAction func searchTextClick(sender: AnyObject) {
if(personTable.alpha == 0){
personTable.alpha = 1
searchActive = true;
searchText(searchText, textDidChange: "")
}else{
personTable.alpha = 0
searchActive = false;
}
}
func tableView(personTable: UITableView!, numberOfRowsInSection section: Int) -> Int
{
return names.count;
}
func tableView(personTable: UITableView!,
cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
let cell:UITableViewCell = UITableViewCell(style:UITableViewCellStyle.Default, reuseIdentifier:"cell")
cell.textLabel!.text = names[indexPath.row]
return cell
}
func searchText(searchText: UITextField, textDidChange newSearch: String) {
let filteredArr = names.filter{$0.containsCharacters(searchText.text)}
self.personTable.reloadData()
}
}
extension String {
func containsCharacters(ch:String)-> Bool{
var err:NSError?
let reg = NSRegularExpression(pattern: ch, options: NSRegularExpressionOptions.CaseInsensitive, error: &err)
if let matches = reg?.matchesInString(self, options: NSMatchingOptions.WithoutAnchoringBounds, range: NSMakeRange(0,count(self))) where matches.count != 0 {
return true
}
return false
}
}
Thx

For good reasons drop down's does not exist in iOS.
You should use a UIPickerView or add add a search field to your tableview.
In the latter example you should implement the UISearchBarDelegate
and use one of it's delegate methods to do you actions. Could look something like this
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText == "" {
self.filteredDkStationsKeys = self.allDkStationsKeys
}else{
self.filteredDkStationsKeys.removeAll(keepCapacity: false)
self.filteredDkStationsKeys = self.allDkStationsKeys.filter { ( $0.lowercaseString.hasPrefix(searchText.lowercaseString) ) }
}
self.tableView.reloadData()
}
Where you can replace the filter stuff with your own filter functions (I show the filteredDkStations in the table). This would give you a table which is getting smaller and smaller as less and less text matches the search input. It looks great!

Related

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.

Error in SearchBar while trying to write some thing using swift3

I'am trying to make a search bar in my table view but I have an error while trying to write any thing in the search bar , The error is (Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier cell1 - must register a nib or a class for the identifier or connect a prototype cell in a storyboard') , I,am making a class to my cell and use it identifier as (cell1) but still the same error , can any one help me to solve this problem using Swift 3 .
This is My Code :
import UIKit
class ProgrTable: UIViewController , UITableViewDataSource , UITableViewDelegate , UISearchBarDelegate {
#IBOutlet weak var ProgTable: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var searchActive : Bool = false
var filtered:[String] = []
var ProgArray = [UIImage]()
var ProgTitle = [String]()
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
ProgTable.dataSource = self
ProgTable.delegate = self
ProgArray = [UIImage(named :"1")!,UIImage(named :"1")!,UIImage(named :"1")!,UIImage(named :"1")!,UIImage(named :"1")!,UIImage(named :"1")!,UIImage(named :"1")!,UIImage(named :"1")!]
ProgTitle = ["ssss ","kkkkkkk","vvvvvvv","yyyyyy","uuuuuu","eeeee","rrrrr","hhhhh","ggggg","fffff","dddd"]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
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 = ProgTitle.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.ProgTable.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if(searchActive) {
return filtered.count
}
return ProgArray.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1" , for: indexPath ) as! ProgrCell
if(searchActive){
cell.progLable.text = filtered[indexPath.row]
} else {
cell.progLable.text = ProgTitle[indexPath.row]
cell.progImage.image = ProgArray[indexPath.row]
}
return cell
}
}
This is My cell code :
import UIKit
class ProgrCell: UITableViewCell {
#IBOutlet weak var progLable: UILabel!
#IBOutlet weak var progImage: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
In StoryBoard, select the cell, and make sure that the identifier is cell1, and that the class has been set to 'ProgrCell'

How can I connect the SearchBar to a different dataSource?

I have a View that has a searchBar and a corresponding TableView below it and I have it working correctly. My question is; is it possible to hook up the SearchBar to a different TableView ? I am new to swift and iOS development and can not find a way to get it done . This image will help illustrate . AS you can see I have a Search Table View Controller and it is working correctly with the Search Bar. What I would like to do now is connect that SearchBar to the Second TableView instead . I would like to do that because eventually i'll have a TableView that will not be connected to the SearchBar, however when a user clicks on the SearchBar a new TableView will cover the original TableView . Below I will show my code for the working TableView, again I would like to connect the SearchBar to the Second
class SearchTableViewController: UITableViewController,UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
struct ApplicationData {
var items: [String]
var filteredItems: [String] = []
init() {
items = ["John","Sam","Oliver"]
filterData(search: "")
}
mutating func filterData(search: String)
{
if search.characters.count>0 {
filteredItems = items.filter({ (item) in
let value1 = item.lowercased()
let value2 = search.lowercased()
let valid = value1.hasPrefix(value2)
return valid
})
}
filteredItems.sort(by: { (value1,value2) in value1 < value2 })
}
}
var AppData = ApplicationData()
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String){
if let text = searchBar.text {
let search = text.trimmingCharacters(in: .whitespaces)
AppData.filterData(search: search)
tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return AppData.filteredItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ZipSearch", for: indexPath)
let data = AppData.filteredItems[indexPath.row]
cell.textLabel?.text = data
return cell
}
}
I am thinking that maybe the tableView.reloadData() piece of code is defaulting to the original tableView but any help would be greatly appreciated .

Why an extension is blocking a touch event in tableViewCells?

My UITableView used to work and sudendly it stopped to act as it was intended. I've found the segues are still doable but not just touching the cell, you need to hold the button and slide and then it does the segue. The delegates methods aren't called when a single touch is done. After being researching for a while, I think the best idea is to post my issue here, because looking in stackoverflow, seems that nobody have a similar issue.
I can click on the shop and the shop gets selected, but I have to maintain the button pressed, not a lot of time, a few miliseconds, almost instantly. The row turns gray but nothing happen. If I release the button then nothing happens too. if I slide then it does the segue. There is nowhere in my code or even in my whole project were I have added a single gesture. I've found an extension in my code that actually adds a gesture to my father viewController custom class. Find it in below
This is my table view code:
//
// showShopsTableView.swift
// yuApp
//
// Created by Alfredo on 16/5/16.
// Copyright © 2016 Alfredo. All rights reserved.
//
import UIKit
import CoreLocation
import MapKit
class showShopsTableView : ViewController, UITableViewDelegate, UITableViewDataSource,UISearchBarDelegate, UISearchResultsUpdating {
var selectedRow : shopsTableViewCell!
var index : Int = 0
#IBOutlet var searchBar: UISearchBar!
#IBOutlet weak var currentTableView: UITableView!
let resultSearchController = UISearchController(searchResultsController:nil)
var searchActive : Bool = false
var filtered:[String] = []
override func viewDidLoad(){
self.currentTableView?.delegate = self;
self.currentTableView?.dataSource = self;
super.viewDidLoad()
//Retrieve data asynchronously
let call = webApi()
call.retrieveOneCityShops{(success) in
self.doTableRefresh()
}
viewDidLayoutSubviews()
}
override func viewDidLayoutSubviews() {
/*
if let rect = self.navigationController?.navigationBar.frame {
let y = rect.size.height + rect.origin.y
self.currentTableView.contentInset = UIEdgeInsetsMake( y, 0, 0, 0)
}*/
}
//Segue Method
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationViewController = segue.destinationViewController as! detailViewShop
sendVariablesToDetailView(destinationViewController)
}
//Set variables in the next view controller before do a segue
func sendVariablesToDetailView(destinationViewController : detailViewShop){
destinationViewController.dataSend = self.selectedRow
destinationViewController.titleText = self.selectedRow.shopName.text
destinationViewController.descriptionText = self.selectedRow.shopDescription.text
destinationViewController.imageOutlet = self.selectedRow.shopImg
destinationViewController.shopCategory = self.selectedRow.label1.text
destinationViewController.brands = self.selectedRow.label2.text
}
//TableView Delegate Methods
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = tableView.cellForRowAtIndexPath(indexPath)! as! shopsTableViewCell
self.performSegueWithIdentifier("cell", sender: self)
}
func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int
{
/*
if(searchActive) {
return filtered.count
}
*/
return TableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : shopsTableViewCell = tableView.dequeueReusableCellWithIdentifier("shopCell", forIndexPath: indexPath) as! shopsTableViewCell
cell.shopName.text = TableData[indexPath.row]
cell.shopDescription.text = values[indexPath.row].address
cell.label1.text = values[indexPath.row].city
cell.label2.text = values[indexPath.row].distance + "Km"
cell.shopImg = UIImageView.init(image: UIImage.init(named: "versace_shop.jpg"))
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 125
}
//SearchBar Delegate Methods
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 = TableData.filter({ (text) -> Bool in
let tmp: NSString = text
let range = tmp.rangeOfString(searchText, options: NSStringCompareOptions.CaseInsensitiveSearch)
return range.location != NSNotFound
})
if(filtered.count == 0){
searchActive = false;
} else {
searchActive = true;
}
self.currentTableView.reloadData()
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
}
func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
func filterContentForSearchText(searchText: String, scope: String = "All") {
currentTableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func doTableRefresh(){
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.currentTableView.reloadData()
return
})
}
}
EDIT: to remove all images that were showing the property userInteractionEnabled to TRUE. I've finally found the source of the issue. It has nothing to do with storyBoard. Is an extension I wrote for my main custom class, viewController (and so parent of this window too). The extension is that one:
import Foundation
import UIKit
// Put this piece of code anywhere you like
extension ViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
}
It was called at the very beginning of my custom viewController class, right after super.viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
// Hide keyboard when tap away
self.hideKeyboardWhenTappedAround()
I've commented super.viewDidLoad() in my showShopsTableView class and now is working just fine.
I'm not really sure to understand why this piece of code is blocking the touch event of my tableView, so if somebody wants to explain it, I will accept his answer. Also some points to my question would be very appreciated if you found it usefull.
In any case, thanks a lot to all that spend a minute here.

Swift - Search Bar Initial Load

I am attempting to have a search functionality within my app. The search would basically search for user input in the Parse class in the backend.
So far the code below works very well for my case except that as soon as the view loads (prior to starting typing anything) it loads all the usernames in the backend in the table rows. As the user types letters, it filters. I want to have all the same functionality, except for showing all users in table rows as soon as view loads. How can this be achieved?
class FriendByUsernameTableViewController: UITableViewController, UISearchBarDelegate, UISearchDisplayDelegate {
var friendObject = FriendClass()
#IBOutlet weak var searchBar: UISearchBar!
var searchActive : Bool = false
var data:[PFObject]!
var filtered:[PFObject]!
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar.delegate = self
search()
}
func search(searchText: String? = nil){
let query = PFQuery(className: "_User")
if(searchText != nil){
query.whereKey("appUsername", containsString: searchText)
}
query.findObjectsInBackgroundWithBlock { (results, error) -> Void in
self.data = results as? [PFObject]!
self.tableView.reloadData()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(self.data != nil){
return self.data.count
}
return 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
let obj = self.data[indexPath.row]
cell.textLabel!.text = obj["appUsername"] as? String
return cell
}
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) {
search(searchText)
}
}
The code is based on this tutorial: http://shrikar.com/parse-search-in-ios-8-with-swift/
try adding
if searchText!.stringByTrimmingCharactersInSet(.whitespaceCharacterSet()) != "" {
query.findObjectsInBackgroundWithBlock()
}
in your search function along with the block of course.
This way you won't query if you click on the search bar or if you tap a bunch of spaces.
Also, if you don't want users to be displayed upon load, don't call search() in your viewDidLoad()

Resources