UISearchbar clearButton forces the keyboard to appear

后端 未结 12 1914
暖寄归人
暖寄归人 2020-11-30 20:40

I have a UISearchBar which acts as a live filter for a table view. When the keyboard is dismissed via endEditing:, the query text and the gray circular \"clear\" button rem

相关标签:
12条回答
  • 2020-11-30 21:12

    This is an old question and I just came across the same issue and managed to solve it the following way:

    When the searchBar:textDidChange: method of the UISearchBarDelegate gets called because of the user tapping the 'clear' button, the searchBar hasn't become the first responder yet, so we can take advantage of that in order to detect when the user in fact intended to clear the search and not bring focus to the searchBar and/or do something else.

    To keep track of that, we need to declare a BOOL ivar in our viewController that is also the searchBar delegate (let's call it shouldBeginEditing) and set it with an initial value of YES (supposing our viewController class is called SearchViewController):

    @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;
        }
    }
    ...
    @end
    

    Later on, in the UISearchBarDelegate, we implement the searchBar:textDidChange: and searchBarShouldBeginEditing: methods:

    - (void)searchBar:(UISearchBar *)bar textDidChange:(NSString *)searchText {
        NSLog(@"searchBar:textDidChange: isFirstResponder: %i", [self.searchBar isFirstResponder]);
        if(![searchBar isFirstResponder]) {
            // user tapped the 'clear' button
            shouldBeginEditing = NO;
            // do whatever I want to happen when the user clears the search...
        }
    }
    
    
    - (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;
    }
    

    Basically, that's it.

    Best

    0 讨论(0)
  • 2020-11-30 21:12

    Of of the search bar delegate calls asks you to accept a change from an old value to a new one - you could detect that the new value was nil, along with the old value being not-nil, and an indicator that the user had not typed anything since the keyboard was last up - then in that case resign first responder for the search bar. Not sure if the keyboard will momentarily display though.

    I have a very similar situation and may try that myself.

    0 讨论(0)
  • 2020-11-30 21:17

    I used a combination of @boliva's answer and also @radiospiel's answer to a different SO question:

    @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
    
    0 讨论(0)
  • 2020-11-30 21:17

    Touching the clear button results in searchText being empty. Another way to achieve this is to check for empty text in - (void)searchBar:(UISearchBar *)bar textDidChange:(NSString *)searchText:

    - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
    {
        if([searchText length] == 0)
        {
            [self dismissSearch];
        }
        else
        {
            self.searchResultsTable.hidden = YES;
            [self handleSearchForString:searchText];
        }
    }
    
    - (void)dismissSearch
    {
        [self.searchBar performSelector: @selector(resignFirstResponder)
                      withObject: nil
                      afterDelay: 0.1];
    
        self.searchResultsTable.hidden = YES;
    }
    
    0 讨论(0)
  • 2020-11-30 21:19

    For those of you using the UISearchController in iOS 8 and up, you'll want to simply subclass the UISearchController. For completeness, you may also want to hide the cancel button, as I did, since clearing the text from the UISearchBar is effectively a cancel on the search. I've added that code below if you want to use it.

    The benefit of this is that you'll be able to use this for any class and any view, rather than requiring a subclass of UIViewController. I'll even include how I initialize my UISearchController at the bottom of this solution.

    FJSearchBar

    This class only needs to be overridden if you want to hide the cancel button as I did. 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:

    1. 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.
    2. 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.
    3. searchBar:textDidChange: is invoked before searchBarShouldBeginEditing:, which is why we handled it in this order.
    4. 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.
    0 讨论(0)
  • 2020-11-30 21:21

    Best solution from my experience is just to put a UIButton (with clear background and no text) above the system clear button and than connect an IBAction

    With autolayout its just more than easy

    - (IBAction)searchCancelButtonPressed:(id)sender {
    
        [self.searchBar resignFirstResponder];
        self.searchBar.text = @"";
    
        // some of my stuff
        self.model.fastSearchText = nil;
        [self.model fetchData];
        [self reloadTableViewAnimated:NO];
    
    }
    
    0 讨论(0)
提交回复
热议问题