I have a view controller in which I dynamically create a UISearchController and assign it to self.navigationItem.searchController.
class MyViewController: UIViewController, UISearchBarDelegate {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.searchController = searchController
searchController.searchBar.delegate = self
// launch the app directly into this search text box
searchController.searchBar.becomeFirstResponder()
}
func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
// I tried this
searchBar.resignFirstResponder()
// and this
searchBar.endEditing(true)
// and finally this
return true
}
}
The keyboard hides away when the user touches anywhere outside the searchbar and the screen goes back to its brightness. That is the expected behavior.
However, when the user taps the [Search] button within the on-screen-keyboard, the keyboard goes away, but the screen is kept dim. None of the sub-views are useable, except if the user taps the search bar again, then the keyboard comes back.
So in short, the only way for the user to continue using the view controller, is not to hit the [search] button, which is counter-intuitive.
am I missing something?
In viewDidLoad(), add the following line:
searchController.obscuresBackgroundDuringPresentation = false
From the documentation: If you use the same view controller to display the searchable content and search results, it is recommended that you set this property to false. The default value of this property is true.
Related
Im running into a problem when I preform a search, I can't get the search bar to reset from it's focused state (The background view is dimmed and the cancel button is still in the search bar. I want to clear the search bar and NOT have the background dimmed).
The view controller has the following (in the view did load):
let searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.delegate = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.placeholder = "Search here"
searchController.searchBar.searchBarStyle = .minimal
self.navigationItem.searchController = searchController
When I tap search, I get have the following:
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.endEditing(true)
searchBar.resignFirstResponder()
searchBar.text = ""
self.navigationItem.searchController?.resignFirstResponder()
}
The searchBar.endEditing(true) doesn't seem to be doing anything. Nor the resignFirstResponder. The background of the view is still dimmed.
For reference, the view controller is embedded in a nav controller, which in part a tab bar view controller (if that matters). And after the search is made, I push another view controller on the stack (hence why I want to clear the search bar and reset it's state when the view controller is shown again).
I've added some pictures to show the problem visually. The second photo is what Im left with after search is pressed when I want to to reset and look like the first photo.
self.navigationItem.searchController?.isActive = false is apparently what I'm looking for
I'm using a search controller in order to implement a search section in an iOS app. I'm using iOS 11 and xCode 10.
My scene is a Table View Controller. I have included a search bar programmatically with the following code:
override func viewDidLoad() {
super.viewDidLoad()
let searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.delegate = self
definesPresentationContext = true
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
}
I have also included a UISearchBar delegate extension:
extension SearchTableViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
tableView.reloadData()
episodeResultsArray = []
let searchTerm = searchBar.text!
let escapedSearchTerm = searchTerm.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
performSearch(forTerm: escapedSearchTerm)
}
}
The performSearch function connects to a database via AlamoFire and returns the results which are then listed in the Table View.
When I run the app, the table appears empty (as it should be initially). When I click on the search bar, it enters the editing state (the navigation bar collapses, the search bar sticks to the top, a Cancel button appears, and a translucent grey box covers the rest of the screen.
When I search for something, the results are succesfully listed in the table. However, even though the results are visible, I still remain in the editing state for the search bar. I need to click on the translucent box to exit the editing state and only then I am able to scroll through the results.
How can I exit the editing state after I click on Search so I'm able to scroll through the table as soon as I get the results?
I tried using searchBar.resignFirstResponder() but this only dismisses the keyboard. I also tried
searchBar.endEditing(true)
but even though the search bar loses focus, I'm still in the editing state.
Just to clarify, I need for this to work as a search from scratch, not as a filter, meaning not just showing a subset of results as I type.
Found the answer. The solution is to add
searchController.isActive = false
I have a search bar implemented using a UISearchController inside my navigation bar. There is also a table view whose top constraint is set to the bottom of the navigation bar.
Desired behaviour: When the cancel button is pressed, the search bar is hidden and the top constraint of the table view returns to what it was before the search bar was removed (see screenshot #1 at the end of this post)
Current behaviour: When the cancel button is pressed, the search bar is gone but the top constraint of the tableView does not change in response (see screenshot #3)
A possible solution to this problem would be to manually update constraints whenever the cancel button is clicked. However, I can't find a way to access the tableView's constraints from the UISearchBarDelegate method searchBarCancelButtonClicked
Code Snippet:
class ViewController: UIViewController {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchBar.delegate = self
/* Adding search button to the navbar */
/* setting tableView constraints */
/* tableView delegate/datasource methods, etc... */
}
#objc func searchButtonTapped(_ sender: UIBarButtonItem) {
setup()
navigationItem.searchController = searchController
}
func setup() {
searchController.hidesNavigationBarDuringPresentation = false
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
}
}
extension UISearchBarDelegate {
public func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
navigationItem.searchController = nil
/* Cannot access tableview constraints from here because extension is outside of the class */
}
}
Before the search button is pressed.
Before the cancel button is pressed.
After the cancel button is pressed.
Add one line code as following:
func searchBarCancelButtonClicked(_ searchBar: UISearchBar){
self.navigationItem.searchController = nil
self.view.setNeedsLayout()
/* Cannot access tableview constraints from here because extension is outside of the class */
}
(YES this is right thing)
func searchBarCancelButtonClicked(_ searchBar: UISearchBar){
self.navigationItem.searchController = nil
self.view.setNeedsLayout()
/* Cannot access tableview constraints from here because extension is outside of the class */
}
Try to update the view inside the navigation controller like this:
func searchBarCancelButtonClicked(_ searchBar: UISearchBar){
navigationItem.searchController = nil // or searchController
navigationController?.view.setNeedsLayout() // invalidate current layout
navigationController?.view.layoutIfNeeded() // force update layout
}
I implemented a search bar inside a navigation controller, it's working fine but the cancel button tap delegate method is not being called. Please help:
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
//This function is not being called
}
let searchBarCnt = UISearchController(searchResultsController: nil)
navigationItem.searchController = searchBarCnt
navigationItem.hidesSearchBarWhenScrolling = true
self.navigationController?.view.backgroundColor = UIColor.white
searchBarCnt.delegate = self
searchBarCnt.searchBar.delegate = self
The function delegate is not being called because you are missing the definesPresentationContext:
Determines which parent view controller's view should be presented over for presentations of type
UIModalPresentationCurrentContext. If no ancestor view controller has this flag set, then the presenter
will be the root view controller.
you may enable such flag in this way:
import UIKit
class ViewController: UIViewController, UISearchBarDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.searchController = UISearchController(searchResultsController: nil)
self.navigationItem.searchController?.searchBar.delegate = self
self.definesPresentationContext = true
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
print("searchBarCancelButtonClicked")
}
}
Note:
without definesPresentationContext, you are not really touching the cancel button (when you try to touch it), you are just dismissing the context (you may notice a "silent" glitch in the background focus), suchlike
a popover is being dismissed.
When you click the Cancel button which appears next to search bar delegate method won't be called.
Try to click on search button on keyboard and check whether cancel button delegate method is getting called or not
In iOS 11 you can put a UISearchController in the NavigationBar with a few lines of code.
I set up everything in the ViewController.swift.
func setupNavBar() {
navigationController?.navigationBar.prefersLargeTitles = true
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = wordViewController
searchController.searchBar.scopeButtonTitles = ["French", "English"]
searchController.searchBar.delegate = wordViewController
navigationItem.searchController = searchController
// Make searchbar persistent
navigationItem.hidesSearchBarWhenScrolling = false
}
In my delegate, the search fires and filters properly. However, if I click either of the scope buttons, they simply disappear. This delegate method is never called. (filter by scope is not actually implemented yet)
extension WordViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
if let searchText = searchBar.text {
print("Scoped changed: \(searchText)")
filteredWordList = wordList.filter({$0.contains(searchText)})
}
}
}
Full source is on Github:
https://github.com/melling/ios_topics/tree/master/NavBarSearch
https://github.com/melling/ios_topics/tree/master/NavBarSearch/NavBarSearch
Try to add this code within setupNavBar():
searchController.dimsBackgroundDuringPresentation = false
I was able to use Artem's solution,
searchController.dimsBackgroundDuringPresentation = false
but I then ran into the problem of no longer being able to click on the background to dismiss the focus on the search bar.
To resolve this, I created my own dimmer uiview (screen size, background color of black with 25% opacity), and added that to the view of the currently presented screen, and listened for a tap. When the user taps it, I fade it out and set
searchController.active = false
In your ViewController, you need to add this line
self.definesPresentationContext = YES;
This tells the UISearchController the presentation context it needs to use, which is defined by your ViewController.
Check this link for more details: http://asciiwwdc.com/2014/sessions/228