UISearchBar Cancel Button

Using a UISearchBar with showCancelButton=YES on iOS 5. Would like the cancel button to stay enabled when the keyboard drops down. Using the following code seems not to work:

    for (id subView in self.searchControl.subviews)
    {
        if ([subView isKindOfClass:[UIButton class]])
        {
            UIButton *cancelButton = (UIButton *)subView;
            [cancelButton setEnabled:YES];
            break;
        }         
    }

The subView is actually a UINavigationButton which appears not to be subclassed off of UIButton. What am I missing here??????? Also cannot find any info on the UINavigationButton class in the Apple docs.

Set your searchbar delegate and than put this code.

- (void) searchBarSearchButtonClicked:(UISearchBar*) theSearchBar
  {
     [theSearchBar resignFirstResponder];
     [theSearchBar setShowsCancelButton:NO animated:YES];
  }
 - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
  {
     [searchBar setShowsCancelButton:YES animated:YES];
  }
  - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
  {
     [searchBar resignFirstResponder];
     [searchBar setShowsCancelButton:NO animated:YES];
  }

Swift 3.0

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
     searchBar.resignFirstResponder()
     searchBar.setShowsCancelButton(false, animated: true)
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
     searchBar.setShowsCancelButton(true, animated: true)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
     searchBar.resignFirstResponder()
     searchBar.setShowsCancelButton(false, animated: true)
}

func searchBar(searchBar: UISearchBar, textDidChange searchText: String) { // Do some search stuff } func searchBarCancelButtonClicked(searchBar: UISearchBar) { // Stop doing the search stuff // and clear the text in the search bar searchBar.text = "" // Hide the cancel button searchBar.showsCancelButton = false // You could also change the position, frame etc of the searchBar }

Swift 5:, Swift 4:

if let btnCancel = searchBar.value(forKey: "cancelButton") as? UIButton {
    btnCancel.isEnabled = true
}

You can use the tint property of searchBar to change the color of searchBar, cancel button’s color will get changed but according to the color of UISearchBar. I can’t be edited manually. But you can always put a custom over it in the interface builder which will hide the native cancel button. And the user will use your custom button as the cancel button of the searchBar.

Just to improve upon what Kurt Spindler said on his post. Though this might not be superior but it is more contained. I use dispatch to do the same thing.

-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar{
    for (UIView *subview in searchBar.subviews)
    {
        if ([subview isKindOfClass:[UIButton class]])
        {
            int64_t delayInSeconds = .001;
            dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
            dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
                UIButton * cancelButton = (UIButton *)subview;
                [cancelButton setEnabled:YES];
            });
            break;
        }
    }
}

This should work for everyone who needs help keep cancel enabled. Make sure that you hide it later either with the cancel or Search clicked.

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar{
    [searchBar resignFirstResponder];
    [searchBar setShowsCancelButton:NO animated:YES];
}

This results in coloring the Cancel button white and the cursor when typing gray. Otherwise it would be white and thus not seen. The searchController is an object of type UISearchController .

I had to solve the same problem in my application. Try doing your above code after a fractional delay, e.g.

[self performSelector:@selector(delayedEnable:) withObject:cancelButton afterDelay:0.001];

- (void)delayedEnable:(UIButton*)button {
    button.enabled = YES;
}

It's ugly, but that's what it took to work for me. Alternatively, if you actually use a UISearchDisplayController to display the results, it should also fix the cancel button behavior for you (I think - I've delved into this issue less).

true to use animation to change the display state of the cancel button, otherwise false. Discussion Cancel buttons are not displayed for apps running on iPad, even when you specify true for the shows Cancel Button parameter

I placed a custom cancel button over the search bar cancel button.

The default behavior for the iOS UISearchbar's ShowsCancelButton property is to display the cancel button as long as the searchbar is highlighted/focused (even if text is empty) and I think the Forms SearchBar should behave the same on iOSI found a thread in the forumshttps://forums.xamarin.com/discussion/59750/searchbar-cancel-button-not-visible-when-text-is-empty.

The UISearchBar is used to search through a list of values. It contains three main components: A field used to enter text. Users can utilize this to enter their search term. A clear button, to remove any text from the search field. A Cancel button, to exit the search function. Implementing the Search Bar

Use this code to set Red color (text, courser, button) searchController.searchBar.tintColor = .red if you want to change cancel button color to white add this code too. UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes([NSForegroundColorAttributeName : UIColor.white], for: .normal)

search Bar Cancel Button Clicked(_:) Tells the delegate that the cancel button was tapped.

Comments
  • As of now, none of the answers explain how you are supposed to find the cancel button. Byte's answer, for example just uses isKindOfClass: UIButton, which the OP has already said doesn't work. Since UINavigationButton is an undocumented class, then does that mean that any messages sent to the object will cause your app to be rejected?
  • If you want to perform something asynchronously, just use dispatch_async. You don't need dispatch_after with an "almost instant delay value". E.g. in this case dispatch_async(dispatch_get_main_queue(), ^(void){...}); works perfectly fine.
  • I believe the 0.001 was necessary based on the answer by Kurt. If that is not the case, then your approach would be perfectly acceptable.
  • This is working like a magic..@Kurt Can you tell me from where you got this? Amazing..upvoted