How to prevent search bar from disappearing on scroll? iOS Swift
Problem is view hierarchy.
Your storyboard is this:
Search Bar is added on TableView.
Correct hierarchy is this:
You change UITableViewController to UIViewController, and appending SearchDisplayController while being careful about view hierarcy.
It will work :)
Comments
-
MasterProgrammer200 over 1 year
I am creating a contact app. I have a scroll bar at the top of my table view.
When I scroll down the search bar disapears.
How to prevent search bar from disappearing on scroll? I want it to stay at the top of the page at all times like in the first picture.
Here is a picture of my storyboard:
Here is my view controller code for if the solution is not in storyboard:
class ViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate, UISearchResultsUpdating { //manages search bar var searchController:UISearchController! var contacts = [Contact]() //array to hold contacts that match the search results var filteredContacts = [Contact]() override func viewDidLoad() { super.viewDidLoad() //initialize the defaults manager class NSUserDefaultsManager.initializeDefaults() //search controller searchController = UISearchController(searchResultsController: nil) searchController.searchBar.sizeToFit() tableView.tableHeaderView = searchController.searchBar definesPresentationContext = true searchController.searchResultsUpdater = self searchController.dimsBackgroundDuringPresentation = false //load the contacts title = "Contacts" tableView.backgroundView = UIImageView(image: UIImage(named: "valblur15")) contacts = [Contact]() let api = ContactAPI() api.loadContacts(didLoadContacts) } override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) //reload the table view to check for new contacts tableView.reloadData() //set the color of the nav bar to valbruna yellow navigationController?.navigationBar.backgroundColor = UIColor(red: 254.0/255.0, green: 196.0/255.0, blue: 37.0/255.0, alpha: 0.95) } //MARK: -Helper Methods // Uupdate searching results func updateSearchResultsForSearchController(searchController: UISearchController) { let searchText = searchController.searchBar.text filterContentForSearchText(searchText) tableView.reloadData() } func filterList() { // should probably be called sort and not filter //sort contacts from a-z contacts.sort() { $0.name < $1.name } //remove contacts whose locations are nil contacts = contacts.filter() { $0.location != "nil"} //remove contacts whose titles phone email and department are all nil contacts = contacts.filter() { if($0.title != "" || $0.phone != "" || $0.email != "" || $0.department != ""){ return true } return false } tableView.reloadData() } func didLoadContacts(contacts: [Contact]){ self.contacts = contacts filterList() tableView.reloadData() } //MARK: -Table View //set number opf sections in table view override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } //delegeate that tells tabel view how many cells to have override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { //return size of array //if searching show count of filtered contacts if (searchController.active){ return self.filteredContacts.count }else{ return self.contacts.count } } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = self.tableView.dequeueReusableCellWithIdentifier("customcell") as! CustomCell //from the customcell class //change color of cell text label cell.textLabel?.textColor = UIColor.blackColor() cell.backgroundColor = UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 0.8) let contact : Contact //if users is searching then return filtered contacts if (searchController.active){ contact = self.filteredContacts[indexPath.row] }else{ contact = self.contacts[indexPath.row] } //handel assignment of text cell.textLabel?.text = contact.name //retrieve from customcell class cell.contact = contact return cell } //MARK: -Search func filterContentForSearchText(searchText: String, scope: String = "Title") { self.filteredContacts = self.contacts.filter({( contact: Contact) -> Bool in //filters contacts array var categoryMatch = (scope == "Title") var stringMatch = contact.name?.rangeOfString(searchText) return categoryMatch && (stringMatch != nil) }) } func searchDisplayController(controller: UISearchController, shouldReloadTableForSearchString searchString: String!) -> Bool { self.filterContentForSearchText(searchString, scope: "Title") return true } func searchDisplayController(controller: UISearchController, shouldReloadTableForSearchScope searchOption: Int) -> Bool { self.filterContentForSearchText(self.searchController!.searchBar.text, scope: "Title") return true } //MARK: -Segue override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if(segue.identifier == "detailview"){ let cell = sender as! CustomCell let detailView = segue.destinationViewController as! DetailViewController detailView.preContact = cell.contact } } }
EDIT 1:
Here are the steps I have taken based off Ryosuke Hiramatsu's solution:
- Command X the table view.
- Add a view.
- Command V the table view into the new view.
- In the view controller, change UITableViewController to UITableView
- Add an outlet to the view controller named table view.
- Remove the overrides in the table view functions.
- Remove this line of code: tableView.tableHeaderView = searchController.searchBar
- Go back to storyboard and move the search bar out of the table view. It'll appear behind the table view.
- Move the table view down and the search bar up.
- Add necessary constraints.
My View now looks like this:
When I scroll down:
My Storyboard:
And finally My updated view controller code:
import UIKit class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchResultsUpdating { @IBOutlet var tableView: UITableView! //manages search bar var searchController:UISearchController! var contacts = [Contact]() //array to hold contacts that match the search results var filteredContacts = [Contact]() override func viewDidLoad() { super.viewDidLoad() //initialize the defaults manager class NSUserDefaultsManager.initializeDefaults() //search controller searchController = UISearchController(searchResultsController: nil) searchController.searchBar.sizeToFit() definesPresentationContext = true tableView.tableHeaderView = searchController.searchBar searchController.searchResultsUpdater = self searchController.dimsBackgroundDuringPresentation = false //load the contacts title = "Valbruna Contacts" tableView.backgroundView = UIImageView(image: UIImage(named: "valblur15")) contacts = [Contact]() let api = ContactAPI() api.loadContacts(didLoadContacts) } override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) //reload the table view to check for new contacts tableView.reloadData() //set the color of the nav bar to valbruna yellow navigationController?.navigationBar.backgroundColor = UIColor(red: 254.0/255.0, green: 196.0/255.0, blue: 37.0/255.0, alpha: 0.95) } //MARK: -Helper Methods // Uupdate searching results func updateSearchResultsForSearchController(searchController: UISearchController) { let searchText = searchController.searchBar.text filterContentForSearchText(searchText) tableView.reloadData() } func filterList() { // should probably be called sort and not filter //sort contacts from a-z contacts.sort() { $0.name < $1.name } //remove contacts whose locations are nil contacts = contacts.filter() { $0.location != "nil"} //remove contacts whose titles phone email and department are all nil contacts = contacts.filter() { if($0.title != "" || $0.phone != "" || $0.email != "" || $0.department != ""){ return true } return false } tableView.reloadData() } func didLoadContacts(contacts: [Contact]){ self.contacts = contacts filterList() tableView.reloadData() } //MARK: -Table View //set number opf sections in table view func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } //delegeate that tells tabel view how many cells to have func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { //return size of array //if searching show count of filtered contacts if (searchController.active){ return self.filteredContacts.count }else{ return self.contacts.count } } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = self.tableView.dequeueReusableCellWithIdentifier("customcell") as! CustomCell //from the customcell class //change color of cell text label cell.textLabel?.textColor = UIColor.blackColor() cell.backgroundColor = UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 0.8) let contact : Contact //if users is searching then return filtered contacts if (searchController.active){ contact = self.filteredContacts[indexPath.row] }else{ contact = self.contacts[indexPath.row] } //handel assignment of text cell.textLabel?.text = contact.name //retrieve from customcell class cell.contact = contact return cell } //MARK: -Search func filterContentForSearchText(searchText: String, scope: String = "Title") { self.filteredContacts = self.contacts.filter({( contact: Contact) -> Bool in //filters contacts array var categoryMatch = (scope == "Title") var stringMatch = contact.name?.rangeOfString(searchText) return categoryMatch && (stringMatch != nil) }) } func searchDisplayController(controller: UISearchController, shouldReloadTableForSearchString searchString: String!) -> Bool { self.filterContentForSearchText(searchString, scope: "Title") return true } func searchDisplayController(controller: UISearchController, shouldReloadTableForSearchScope searchOption: Int) -> Bool { self.filterContentForSearchText(self.searchController!.searchBar.text, scope: "Title") return true } //MARK: -Segue override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if(segue.identifier == "detailview"){ let cell = sender as! CustomCell let detailView = segue.destinationViewController as! DetailViewController detailView.preContact = cell.contact } }
}
My Problem now is that only the first search bar is stationary and it doesn't search when I type in it. The second search bar disappears on scroll. Also when I click a name it no longer segues to the next view.