Question

I've got a UISearchBar in my interface and I want to customise the behaviour of the the small clear button that appears in the search bar after some text has been entered (it's a small grey circle with a cross in it, appears on the right side of the search field).

Basically, I want it to not only clear the text of the search bar (which is the default implementation) but to also clear some other stuff from my interface, but calling one of my own methods.

I can't find anything in the docs for the UISearchBar class or the UISearchBarDelegate protocol - it doesn't look like you can directly get access to this behaviour.

The one thing I did note was that the docs explained that the delegate method:

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText;

is called after the clear button is tapped.

I initially wrote some code in that method that checked the search bar's text property, and if it was empty, then it had been cleared and to do all my other stuff.

Two problems which this though:

Firstly, for some reason I cannot fathom, even though I tell the search bar to resignFirstResponder at the end of my method, something, somewhere is setting it back to becomeFirstResponder. Really annoying...

Secondly, if the user doesn't use the clear button, and simply deletes the text in the bar using the delete button on the keyboard, this method is fired off and their search results go away. Not good.

Any advice or pointers in the right direction would be great!

Thanks!

Was it helpful?

Solution

The answer which was accepted is incorrect. This can be done, I just figured it out and posted it in another question:

UISearchbar clearButton forces the keyboard to appear

Best

OTHER TIPS

Found the better solution for this problem :)

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
    if ([searchText length] == 0) {
        [self performSelector:@selector(hideKeyboardWithSearchBar:) withObject:searchBar afterDelay:0];
    }
}

- (void)hideKeyboardWithSearchBar:(UISearchBar *)searchBar{   
    [searchBar resignFirstResponder];   
}

I've got this code in my app. Difference is that I don't support 'live search', but instead start searching when the user touches the search button on the keyboard:

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
    if ([searchBar.text isEqualToString:@""]) {
        //Clear stuff here
    }
}

Swift version handling close keyboard on clear button click :

func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
    if searchText.characters.count == 0 {
        performSelector("hideKeyboardWithSearchBar:", withObject:searchBar, afterDelay:0)
    }
}

func hideKeyboardWithSearchBar(bar:UISearchBar) {
    bar.resignFirstResponder()
}

I would suggest using the rightView and rightViewMode methods of UITextField to create your own clear button that uses the same image. I'm assuming of course that UISearchBar will let you access the UITextField within it. I think it will.
Be aware of this from the iPhone OS Reference Library:

If an overlay view overlaps the clear button, however, the clear button always takes precedence in receiving events. By default, the right overlay view does overlap the clear button.

So you'll probably also need to disable the original clear button.

Since this comes up first, and far as I can see the question wasn't really adequately addressed, I thought I'd post my solution.

1) You need to get a reference to the textField inside the searchBar 2) You need to catch that textField's clear when it fires.

This is pretty simple. Here's one way.

a) Make sure you make your class a , since you will be using the delegate method of the textField inside the searchBar. b) Also, connect your searchBar to an Outlet in your class. I just called mine searchBar. c) from viewDidLoad you want to get ahold of the textField inside the searchBar. I did it like this.

UITextField *textField = [self.searchBar valueForKey:@"_searchField"];
if (textField) {
    textField.delegate = self;
    textField.tag = 1000;
}

Notice, I assigned a tag to that textField so that I can grab it again, and I made it a textField delegate. You could have created a property and assigned this textField to that property to grab it later, but I used a tag.

From here you just need to call the delegate method:

-(BOOL)textFieldShouldClear:(UITextField *)textField {
if (textField.tag == 1000) {
    // do something
    return YES;
}
return NO;

}

That's it. Since you are referring to a private valueForKey I can't guarantee that it will not get you into trouble.

You could try this:

- (void)viewDidLoad
{
    [super viewDidLoad];
    for (UIView *view in searchBar.subviews){
        for (UITextField *tf in view.subviews) {
            if ([tf isKindOfClass: [UITextField class]]) {
                tf.delegate = self;
                break;
            }
        }
    }
}

- (BOOL)textFieldShouldClear:(UITextField *)textField {
     // your code
    return YES;
}

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

- (IBAction)searchCancelButtonPressed:(id)sender {

    [self.searchBar resignFirstResponder];
    self.searchBar.text = @"";

    // some of my stuff
    self.model.fastSearchText = nil;
    [self.model fetchData];
    [self reloadTableViewAnimated:NO];

}

Wasn't able to find a solution here that didn't use a private API or wasn't upgrade proof incase Apple changes the view structure of the UISearchBar. Here is what I wrote that works:

- (void)viewDidLoad {
    [super viewDidLoad];

    UITextField* textfield = [self findTextFieldInside:self.searchBar];
    [textfield setDelegate:self];
}

- (UITextField*)findTextFieldInside:(id)mainView {
    for (id view in [mainView subviews]) {
        if ([view isKindOfClass:[UITextField class]]) {
            return view;
        }

        id subview = [self findTextFieldInside:view];
        if (subview != nil) {
            return subview;
        }
    }

    return nil;
}

Then implement the UITextFieldDelegate protocol into your class and overwrite the textFieldShouldClear: method.

- (BOOL)textFieldShouldClear:(UITextField*)textField {
    // Put your code in here.
    return YES;
}

Edit: Setting the delegate on the textfield of a search bar in iOS8 will produce a crash. However it looks like the searchBar:textDidChange: method will get called on iOS8 on clear.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top