My entire app interface is made programatically from ViewController.swift
Inside the view is UISearchBar. What are the names of functions which I can override in order to run my own code when UISearchBar is tapped on, when the keyboard's Search button is pressed and when UISearchBar's Cancel button is pressed?
If they don't exist, can I trigger my own functions at those times?
You'll want to add conformance to the UISearchBarDelegate protocol to your view controller:
extension ViewController : UISearchBarDelegate {
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
// ...
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
// ...
}
}
and then set the search bar's delegate to self.
If you use the UISearchBarDelegate you can implement the following methods...
//Becomes first responder
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar;
//Editing begins
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar;
//Cancel button pressed
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar;
Related
For some reason when I click the circle x cancel button in a UISearchBar the searchBarCancelButtonClicked event is not firing, it worked in a swift 2 project but not in this swift 3 one.
I am now extending my view controller instead of the inline class way but I believe that is working as the searchBarSearchButtonClicked event does work. Here is what I have so far:
extension MyViewController: UISearchBarDelegate {
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
print("here?")
searchBar.resignFirstResponder()
handleCancelSearch()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
if let searchText = searchBar.text {
performSearchUsing(term:searchText)
}
}
}
The print is not logged and the function not called. Am I missing something silly?
Maybe you are missing something silly as I did, the "circle x" is not actually the cancel button, and I thought that too, the cancel button comes disabled by default, you can activate it via storyboard on the attributes of the searchBar or you can do it programmatically with:
searchBar.showsCancelButton = true
After that, the method should work.
If any one still looking for the option how act on "X" button. use below method which is available in
UISearchBarDelegate
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
{
if searchText.count == 0
{ // Hide content
}
}
Note : it is not cannel button its clear text button which is visible after typing something in searchBar
I am trying to reproduce the functionality that can be seen in the contacts app on the iphone. I have a UISearchBar that dismisses the keyboard when the search button is clicked. This however deactivitates the cancel button and it requires 2 touches to activate. On the contacts app it is not deactivated when the search button is clicked and the keyboard is dismissed.
So what I am asking is how to dismiss the keyboard without deactivating the cancel button on the uiSearchBar?
I have tried
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
//Some other code
//I have Tried
//Attempt 1
self.searchBar.endEditing(true)
//Attempt 2
self.searchBar.resignFirstResponder()
//Attempt 3
var textFieldInsideSearchBar = searchBar.valueForKey("searchField") as? UITextField
textFieldInsideSearchBar.endEditing(true)
}
Delegate method parses you searchBar, so you do not have to use self.searchBar, that might be one of the issues. I usually use logic from your "Attempt 2".
You can try to implement this:
func searchBarTextDidEndEditing(_ searchBar: UISearchBar)
And call searchBar.resignFirstResponder().
If it does not work, then try to implement this method and return true:
func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool
If you are using UISearchBar in combination with UISearchDisplatController, then try this method on searchDisplayController:
func setActive(_ visible: Bool, animated animated;: Bool)
It quite bit tricky.
Try,
[self.searchBar resignFirstResponder];
[(UIButton *)[self.searchBar valueForKey:#"_cancelButton"] setEnabled:YES];
I want to use search bar in my app. But I couldn't find any tutorial for this.
My question is simple: How can I get search bar text when user preses to enter button ?
I need something like this in my view controller:
override func userPressedToEnter(text: String) {
println("User entered: \(text)")
}
How can I do this in swift ?
Assuming you have a simple search bar in your storyboard, make sure you have it connected as an outlet. Then use this as an example. Use UISearchBarDelegate the reference to learn more about delegate methods available to you.
import UIKit
class ViewController: UIViewController, UISearchBarDelegate {
#IBOutlet var searchBar:UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
searchBar.delegate = self
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
print("searchText \(searchText)")
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
print("searchText \(searchBar.text)")
}
}
I would take a look at the UISearchBarDelegate protocol:
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UISearchBarDelegate_Protocol/index.html
Make your view controller class conform to this protocol and you will have everything you need to interact with your search bar. Alternatively you can get at the search bar text field but Apple gives you a much cleaner, nicer, event driven way via this protocol.
Assuming you have a tableview that you're searching, add a Search Bar and Search Controller to the tableview in the storyboard. That'll hook up all the data source / delegate connections that you need.
Then in your tableview you can use:
func searchDisplayController(controller: UISearchDisplayController!, shouldReloadTableForSearchString searchString: String!) -> Bool {
doStuffWithSearchText(searchBar.text, scope: 0)
}
which will get called whenever they change the text in the search bar. It's common to update the data that's displayed every time they change the text but if you need to do it only when they tap on the search button use this function instead:
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
doStuffWithSearchText(searchBar.text, scope: 0)
}
And you can get the text from the search results controller:
controller.searchBar.text
Or from the search bar:
searchBar.text
If you're not using a tableview controller:
Add a search bar
Hook up your view controller as the search bar's delegate
Then use the searchBarSearchButtonClicked: function to handle when they tap the "Search" button or searchBar(searchBar: UISearchBar, textDidChange searchText: String) to handle w
I wrote a tutorial on doing it with a table view controller that has all the gritty details: Adding a Search Bar to a Table View in Swift
My goal is to prevent the cancel button from appearing in a search bar in a UISearchController. I started with Apple's Table Search with UISearchController sample code and hid the cancel button as seen in the code snip below. However, when the user taps in the text field, the cancel button still appears. Any help?
override func viewDidLoad() {
super.viewDidLoad()
resultsTableController = ResultsTableController()
searchController = UISearchController(searchResultsController: resultsTableController)
searchController.searchResultsUpdater = self
searchController.searchBar.sizeToFit()
tableView.tableHeaderView = searchController.searchBar
searchController.searchBar.delegate = self
//Hide cancel button - added by me
searchController.searchBar.showsCancelButton = false
...
I think there are three ways of achieving that:
Override searchDisplayControllerDidBeginSearch and use the following code:
searchController.searchBar.showsCancelButton = false
Subclass UISearchBar and override the layoutSubviews to change that var when the system attempts to draw it.
Register for keyboard notification UIKeyboardWillShowNotification and apply the code in point 1.
Of course can always implement your search bar.
For iOS 8, and UISearchController, use this delegate method from UISearchControllerDelegate:
func didPresentSearchController(searchController: UISearchController) {
searchController.searchBar.showsCancelButton = false
}
Don't forget to set yourself as the delegate: searchController.delegate = self
Simply subclass UISearchController & UISearchBar.
class NoCancelButtonSearchController: UISearchController {
let noCancelButtonSearchBar = NoCancelButtonSearchBar()
override var searchBar: UISearchBar { return noCancelButtonSearchBar }
}
class NoCancelButtonSearchBar: UISearchBar {
override func setShowsCancelButton(_ showsCancelButton: Bool, animated: Bool) { /* void */ }
}
The following github project subclasses UISearchBar which is presented as solution 2:
https://github.com/mechaman/CustomSearchControllerSwift
On top of it, it also subclasses UISearchController to enable one to put the search bar in places other than the tableView header!
Hope this helps.
This was the simplest solution I could come up with in Swift.
Custom search controller:
class CustomSearchController: UISearchController {
var _searchBar: CustomSearchBar
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
self._searchBar = CustomSearchBar()
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
override init(searchResultsController: UIViewController?) {
self._searchBar = CustomSearchBar()
super.init(searchResultsController: searchResultsController)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var searchBar: UISearchBar {
return self._searchBar
}
}
Custom search bar:
class CustomSearchBar: UISearchBar {
override func setShowsCancelButton(showsCancelButton: Bool, animated: Bool) {
// do nothing
}
}
The most important piece of this was to only create the _searchBar object once in init vs. creating it inside of the stored property.
Just subclass your UISearchController and do the following:
class CustomSearchController: UISearchController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
searchBar.showsCancelButton = false
}
}
This was the easiest solution I could came up with in order to solve the flashing cancel-button issue.
TL;DR:
Subclassing UISearchBar and overriding setShowsCancelButton: and setShowsCancelButton:animated: hides the cancel button.
SOLUTION
I set active to NO if the search bar is not the first responder (keyboard is not active and displayed), since that is effectively a cancel command.
FJSearchBar
Marking searchController.searchBar.showsCancelButton = NO doesn't seem to work in iOS 8. I haven't tested iOS 9.
FJSearchBar.h
Empty, but placed here for completeness.
#import UIKit;
#interface FJSearchBar : UISearchBar
#end
FJSearchBar.m
#import "FJSearchBar.h"
#implementation FJSearchBar
- (void)setShowsCancelButton:(BOOL)showsCancelButton {
// do nothing
}
- (void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated {
// do nothing
}
#end
FJSearchController
Here's where you want to make the real changes. I split the UISearchBarDelegate into its own category because, IMHO, the categories make the classes cleaner and easier to maintain. If you want to keep the delegate within the main class interface/implementation, you're more than welcome to do so.
FJSearchController.h
#import UIKit;
#interface FJSearchController : UISearchController
#end
#interface FJSearchController (UISearchBarDelegate) <UISearchBarDelegate>
#end
FJSearchController.m
#import "FJSearchController.h"
#import "FJSearchBar.h"
#implementation FJSearchController {
#private
FJSearchBar *_searchBar;
BOOL _clearedOutside;
}
- (UISearchBar *)searchBar {
if (_searchBar == nil) {
// if you're not hiding the cancel button, simply uncomment the line below and delete the FJSearchBar alloc/init
// _searchBar = [[UISearchBar alloc] init];
_searchBar = [[FJSearchBar alloc] init];
_searchBar.delegate = self;
}
return _searchBar;
}
#end
#implementation FJSearchController (UISearchBarDelegate)
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
// if we cleared from outside then we should not allow any new editing
BOOL shouldAllowEditing = !_clearedOutside;
_clearedOutside = NO;
return shouldAllowEditing;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// hide the keyboard since the user will no longer add any more input
[searchBar resignFirstResponder];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (![searchBar isFirstResponder]) {
// the user cleared the search while not in typing mode, so we should deactivate searching
self.active = NO;
_clearedOutside = YES;
return;
}
// update the search results
[self.searchResultsUpdater updateSearchResultsForSearchController:self];
}
#end
Some parts to note:
I've put the search bar and the BOOL as private variables instead of properties because
They're more lightweight than private properties.
They don't need to be seen or modified by the outside world.
We check whether the searchBar is the first responder. If it's not, then we actually deactivate the search controller because the text is empty and we're no longer searching. If you really want to be sure, you can also ensure that searchText.length == 0.
searchBar:textDidChange: is invoked before searchBarShouldBeginEditing:, which is why we handled it in this order.
I update the search results every time the text changes, but you may want to move the [self.searchResultsUpdater updateSearchResultsForSearchController:self]; to searchBarSearchButtonClicked: if you only want the search performed after the user presses the Search button.
Swift:
The following worked for me, added under viewDidLoad, because I never wanted that button:
let searchBarStyle = searchBar.value(forKey: "searchField") as? UITextField
searchBarStyle?.clearButtonMode = .never
Make sure to add the ID for the searchBar in the storyboard.
Use UISearchControllerDelegate.
func willPresentSearchController(_ searchController: UISearchController) {
searchController.searchBar.setValue("", forKey:"_cancelButtonText")
}
In my project I'm using a UITableViewController with an internal UISearchController to filter the data in my tableView.
I have no problem to filter the data but I need to make a date of my tableView reload when I click on the CANCEL button UISearchController but I can not find the delegate method for this ...
Can you help me understand how to solve this problem?
You need to set the UISearchController searchBar's delegate. Once you have done this, the addition of the delegate method searchBarCancelButtonClicked: will properly be called.
self.searchController.searchBar.delegate = self;
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
}
If you implement UISearchResultsUpdating protocol, you can know that cancelled is triggered when active is false.
func updateSearchResultsForSearchController(searchController: UISearchController) {
if !searchController.isActive {
print("Cancelled")
}
}
Swift 5
searchBar.delegate = self
.......
extension YourClass: UISearchBarDelegate {
func searchBarCancelButtonClicked(_ searchBar: UISearchBar){}
}