Swift - Search Bar Initial Load - ios

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()

Related

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'

Issues setting up searchdisplaycontroller with tableview - Swift

I'm trying to setup the searchdisplaycontroller up for my tableview and am experiencing a couple of issues when declaring my iboutlet for the tableview itself. Attached is a screenshot as well as my code. Any insight much appreciated.
Code Errors Img
import UIKit
class DataTableExercisesTableViewController: UITableViewController, UISearchBarDelegate {
var exercises = ["Abs", "Arms", "Back", "Chest", "Legs", "Shoulders", "Triceps"]
var searchActive : Bool = false
#IBOutlet var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var filtered:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
tableView.delegate = self
tableView.dataSource = self
searchBar.delegate = self
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchActive) {
return filtered.count
}
return exercises.count;
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! as UITableViewCell;
if(searchActive){
cell.textLabel?.text = filtered[indexPath.row]
} else {
cell.textLabel?.text = exercises[indexPath.row];
}
return cell;
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
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 = exercises.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.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Have you tried removing the IBoutlet completely? (Both from storyboard connections and in your code). I think this will solve your issue.

Swift tableView.reloadData() is not working

Solution : https://stackoverflow.com/a/39638032/1106035
I am new to Swift, I need to reload my records when I click on the UIButton action. For me reload method is stop working. I tried in all the possible ways as following:
Here is the function that I call when I tap the button
#IBAction func refresh(sender: AnyObject) {
// I tried this one but doesn't works
dispatch_async(dispatch_get_main_queue()) {
self.tblNotes.reloadData()
}
// This one too doesn't works for me
self.tblNotes.reloadData()
//Neither this
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tblNotes.reloadData()
})
}
Below one is my entire Class
class ListaTrmTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate , UISearchDisplayDelegate, EditNoteViewControllerDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet var tblNotes: UITableView!
var arrNotes: Array<CKRecord> = []
var editedNoteRecord: CKRecord!
var selectedNoteIndex: Int!
var searchActive : Bool = false
var filtered:Array<CKRecord> = []
var notesArray = [ListaTrmTableViewController]()
override func viewDidLoad() {
super.viewDidLoad()
tblNotes.delegate = self
tblNotes.dataSource = self
searchBar.delegate = self
fetchNotes()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
if(searchActive) {
return filtered.count
}
return arrNotes.count
}
//Cell height size
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 50.0
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedNoteIndex = indexPath.row
performSegueWithIdentifier("viewControllerSg", sender: self)
}
//Segue to other ViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "viewControllerSg" {
let editNoteViewController = segue.destinationViewController as! ViewController
if let index = selectedNoteIndex {
editNoteViewController.editedNoteRecord = arrNotes[index]
}
if(searchActive){
editNoteViewController.editedNoteRecord = filtered[selectedNoteIndex]
}
}
}
// Retrive data from CloudKit
func fetchNotes() {
let container = CKContainer.defaultContainer()
let privateDatabase = container.publicCloudDatabase
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Notes", predicate: predicate)
query.sortDescriptors = [NSSortDescriptor(key: "Title", ascending: true)]
privateDatabase.performQuery(query, inZoneWithID: nil) { (results, error) -> Void in
if error != nil {
println(error)
}
else {
println(results)
for result in results {
self.arrNotes.append(result as! CKRecord)
}
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.tblNotes.reloadData()
self.tblNotes.hidden = false
})
}
}
}
func didSaveNote(noteRecord: CKRecord, wasEditingNote: Bool) {
if !wasEditingNote {
arrNotes.append(noteRecord)
}
else {
arrNotes.insert(noteRecord, atIndex: selectedNoteIndex)
arrNotes.removeAtIndex(selectedNoteIndex + 1)
selectedNoteIndex = nil
}
if tblNotes.hidden {
tblNotes.hidden = false
}
tblNotes.reloadData()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("idCellNote", forIndexPath: indexPath) as! UITableViewCell
if(searchActive){
let noteRecord: CKRecord = filtered[indexPath.row]
cell.textLabel?.text = noteRecord.valueForKey("Atitulo") as? String
} else {
let noteRecord: CKRecord = arrNotes[indexPath.row]
cell.textLabel?.text = noteRecord.valueForKey("Atitulo") as? String
}
return cell
}
// Search functions
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 = arrNotes.filter ({ (note) -> Bool in
let titles = note.objectForKey("Atitulo") as? String
//proceed as per normal
let range = titles!.rangeOfString(searchText, options: NSStringCompareOptions.CaseInsensitiveSearch)
// I returned false to isolated the problem
if let range = range { return true} else { return false}
})
if(filtered.count == 0){
searchActive = false;
} else {
searchActive = true;
}
self.tblNotes.reloadData()
}
// The big problem is here
#IBAction func refresh(sender: AnyObject) {
// I tried this one but don't works
dispatch_async(dispatch_get_main_queue()) {
self.tblNotes.reloadData()
}
// This one don't works too
self.tblNotes.reloadData()
//Neither this
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tblNotes.reloadData()
})
}
First, you need to make sure that you have set the delegate and datasource from Storyboard where your TableView exists.
Second, I think you are trying to reload data that if you don't get successfully from cloudkit because of network problem. So,tableView.reloadData() wont bring you anything until you fetch data from cloud. So, try to insert fetchNotes() inside main thread so your view will refresh.
#IBAction func refresh(sender: AnyObject) {
dispatch_async(dispatch_get_main_queue()) {
self.fetchNotes()
}
}
You need to make sure the View Controller is the delegate and data source for the UITable. This can be done through the Storyboard.

Implementing a Search Text Box in Swift

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!

Swift Ios Parse SearchBar Complication

I was recently following a tutorial which shows an early version of Swift being used to create a table of users from Parse with a search function. The following code has allowed me to display all users in a table as I would like, but does not update the table once search text has been put in the search bar. (There are two user arrays because I am relatively new to programming and was following two different tutorials to help make this table). Any help is greatly appreciated!!
var userArray: [String] = []
var userList:NSMutableArray = NSMutableArray()
#IBOutlet weak var searchBar: UISearchBar! = UISearchBar()
func loadUsers(name:String){
var findUsers:PFQuery = PFUser.query()
if !name.isEmpty {
findUsers.whereKey("name", containsString: name)
}
findUsers.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error:NSError!) -> Void in
self.userList = NSMutableArray(array: objects)
self.tableView.reloadData()
}
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
loadUsers(searchText)
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
loadUsers("")
}
override func viewDidLoad() {
super.viewDidLoad()
loadUsers("")
searchBar.delegate = self
self.navigationItem.hidesBackButton = true
var query = PFUser.query()
query.whereKey("username", notEqualTo: PFUser.currentUser().username)
var users = query.findObjects()
for user in users {
userArray.append(user["name"] as String)
println(userArray)
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return userArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
if userArray.count >= 1 {
cell.textLabel?.text = userArray[indexPath.row]
}
return cell
}
You will have to create a new array which holds the data which hold's the results in the searchBar.
Besides, it's better to create a bool variable to handle what's the data showing in the screen (true for search results, false for loadUsers).
This article can help you: http://shrikar.com/blog/2015/02/16/swift-ios-tutorial-uisearchbar-and-uisearchbardelegate

Resources