Dismissing keyboard from UISearchBar when the X button is tapped
I'm using the UISearchBar (but not the SearchDisplayController that's typically used in conjunction) and I'd like to dismiss the keyboard when you hit the 'X' button.
I've followed TomSwift's suggestion on getting called when the 'X' is tapped and that works great. But resigning first responder from the text field and also invoking in the开发者_Go百科 UISearchBar instance, both with resignFirstResponder
, won't make the keyboard go away.
Is there a way to get rid of the keyboard when the user has tapped the X button?
Here's what I did to get the 'Clear' notify:
- (void)viewDidLoad:
{
for (UIView* v in searchBar.subviews)
{
if ( [v isKindOfClass: [UITextField class]] )
{
UITextField *tf = (UITextField *)v;
tf.delegate = self;
break;
}
}
}
Then I have my class setup to implement both UISearchBarDelegate and UITextFieldDelegate.
Having the class serve as the textfield delegate allows me to get this call:
- (BOOL)textFieldShouldClear:(UITextField *)textField
{
[textField resignFirstResponder];
[self.searchBar resignFirstResponder];
return YES;
}
I've tried everything I can think of. The last thing I'm trying is to find a way to issue the 'searchBarCancelButtonClicked' that UISearchDelegate will invoke on my Controller class but not I'm sure how I could do this since the UISearchBar doesn't seem to have any direct methods to invoke with this name.
Toms answer got me thinking. If it is that the search bar is not yet the firstResponder when the user clicks the clear button we can just wait until it is, and then have it resignFirstResponder; i.e. along the lines of:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[self performFilteringBySearchText: searchText]; // or whatever
// The user clicked the [X] button or otherwise cleared the text.
if([searchText length] == 0) {
[searchBar performSelector: @selector(resignFirstResponder)
withObject: nil
afterDelay: 0.1];
}
}
Works like a charm, and less hacky than Tom's IMHO.
This works:
[searchBar performSelector:@selector(resignFirstResponder) withObject:nil afterDelay:0.1];
Updated for SWIFT 3:
Suppose that the user has entered a string in searchfield and clicks x the following code works to hide keyboard when x is pressed
`
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
{
if searchBar.text == nil || searchBar.text == ""
{
searchBar.perform(#selector(self.resignFirstResponder), with: nil, afterDelay: 0.1)
}
}
`
Swift 2 version:
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
// The user clicked the [X] button or otherwise cleared the text.
if (searchText.characters.count == 0) {
searchBar.performSelector("resignFirstResponder", withObject: nil, afterDelay: 0.1)
}
}
Update:
Well, this is a total hack but I was able to make it work. Basically the code invokes the handler for the cancel button. To make it work I had to invoke the selector with a delay, and I'm not sure why this had to be. Also, I had to write an accessor for the cancel button just like you did for the text field.
Again, this is a total hack. I'm not sure I'd do this myself in an app.
// this in the context of the search bar
- (UIButton*) cancelButton
{
for (UIView* v in self.subviews)
{
if ( [v isKindOfClass: [UIButton class]] )
return (UIButton*)v;
}
return nil;
}
// this is the textField delegate callback
- (BOOL)textFieldShouldClear:(UITextField *)textField
{
[textField resignFirstResponder];
UIButton* cb = _searchBar.cancelButton;
NSObject* target = [[cb allTargets] anyObject];
NSArray* actions = [cb actionsForTarget: target forControlEvent:UIControlEventTouchUpInside];
NSString* selectorName = [actions objectAtIndex:0];
SEL selector = NSSelectorFromString( selectorName );
[target performSelector: selector withObject: cb afterDelay: 0.1];
return YES;
}
Original answer:
How do you get the clear 'X' button to display in the first place? In my test case I dont see it displaying...
Try doing a resignFirstResponder on the searchBar, not the textField.
You can resignFirstResponder on the click of cancel Button as.
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
SearchBar.showsCancelButton =NO;
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
[SearchBar resignFirstResponder];
}
I found this in a previous question:
UISearchbar clearButton forces the keyboard to appear
It should do exactly what you want to do.
Try to avoid
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
method in your code we can solve this
I used a combination of @radiospiel's answer and also the answer that @Tozar linked to:
@interface SearchViewController : UIViewController <UISearchBarDelegate> {
// all of our ivar declarations go here...
BOOL shouldBeginEditing;
....
}
...
@end
@implementation SearchViewController
...
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
...
shouldBeginEditing = YES;
}
}
...
- (void) searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchText {
// TODO - dynamically update the search results here, if we choose to do that.
if (![searchBar isFirstResponder]) {
// The user clicked the [X] button while the keyboard was hidden
shouldBeginEditing = NO;
}
else if ([searchText length] == 0) {
// The user clicked the [X] button or otherwise cleared the text.
[theSearchBar performSelector: @selector(resignFirstResponder)
withObject: nil
afterDelay: 0.1];
}
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)bar {
// reset the shouldBeginEditing BOOL ivar to YES, but first take its value and use it to return it from the method call
BOOL boolToReturn = shouldBeginEditing;
shouldBeginEditing = YES;
return boolToReturn;
}
@end
Shouldn't UI changes be made on the main thread instead of using performselector:WithObject:afterDelay:
?
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if (searchText.length == 0) {
[searchBar performSelectorOnMainThread:@selector(resignFirstResponder) withObject:nil waitUntilDone:NO];
}
}
EDIT: Actually, the below breaks the delegate that is connected to UISearchBar. Just subclass UISearchBar and overwrite the UITextField Delegate method.
===========================
I had to do this to get access to the UITextView
for (UIButton* v in [self.searchBar.subviews[0] subviews])
{
if ( [v isKindOfClass: [UITextField class]] )
{
UITextField *tf = (UITextField *)v;
tf.delegate = self;
break;
}
}
Credit to Maxhs for original answer : This is swift 2.2 version : Work like charm for me
if searchBar.text == "" {
dispatch_async(dispatch_get_main_queue(), {
self.searchBar.resignFirstResponder()
})
}
Another point of view for clear text flow (similar to @TomSwift answer but clearer for me and less tricky). Also, I need to hide Cancel button after exit from the search bar, implement live search (after each symbol) and cover table before user complete search.
//self.searchHoverView can cover table view
//performSearchWithSearchBar: method for performing search
#pragma mark - UISearchBarDelegate
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
[searchBar setShowsCancelButton:YES animated:YES];
self.searchHoverView.hidden = NO;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (searchText.length) {
[self performSearchWithSearchBar:searchBar];
} else {
UIButton *button;
for (UIView *subView in [searchBar subviews]) {
for (UIView *view in [subView subviews]) {
if ([view isKindOfClass:[UIButton class]]) {
button = (UIButton *)view;
break;
}
}
}
if (button) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[button sendActionsForControlEvents:UIControlEventTouchUpInside];
});
}
}
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
self.searchBar.text = nil;
[self.searchBar setShowsCancelButton:NO animated:YES];
[self.searchBar resignFirstResponder];
self.searchHoverView.hidden = YES;
}
When you click the 'x' button, the string in the search bar text field becomes an empty collection of characters.
Checking the length of the collection helps to detect when the 'x' button has been clicked.
I had a similar issue and this solution worked for me:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
//let searchText = searchText.trimmingCharacters(in: .whitespaces)
if searchText.isEmpty{
DispatchQueue.main.async { [weak self] in
guard let self = self else{ return }
self.searchBar.resignFirstResponder()
}
}
}
精彩评论