28

I have a UISearchDisplayController and UISearchBar hooked up to my ViewController via Outlets from my nib.

I'd like to hide the cancel button so that the user never sees it. The problem is that the following code hides the button, but only after displaying it to the user for a millisecond (e.g., it flashes on the simulator and device and then disappears out of view).

- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller 
{
    controller.searchBar.showsCancelButton = NO;
}

Is there a better way to hide it?

ArtSabintsev
  • 5,170
  • 10
  • 41
  • 71

11 Answers11

39

I managed to hide the "Cancel" button by subclassing UISearchBar and override this method:

-(void)layoutSubviews{
    [super layoutSubviews];
    [self setShowsCancelButton:NO animated:NO];
}
Nim Gat
  • 650
  • 6
  • 5
  • 1
    This works! I create a view controller with the search functions and then put the whole view controller inside a popover for iPad. Obviously, the "Cancel" button is not necessary for iPad popover, so I am looking for a non-cancle-button solution, and this one works! – Wayne Liu Aug 23 '12 at 08:05
  • 2
    This works just great! I think I tried every other way described to fix this to avoid subclassing, but wasn't able to get it done! But this fixed it flawlessly! Thanks! – Kent Robin Feb 06 '13 at 07:46
  • This didn't work for me, I used the notification way by BFeher – paulvs Feb 21 '14 at 12:43
  • 5
    You can use your view controller's `-viewWillLayoutSubviews` method instead of subclassing `UISearchBar`. That worked for me! – maxkonovalov Sep 12 '14 at 08:17
  • Why not work for me? The cancel button always missing – Gank Jan 09 '15 at 09:04
  • @maxkonovalov solution is much easier – emem Sep 20 '15 at 22:17
  • 3
    While this worked, it caused my Caret to stop appearing after the search controller went inactive then active again. My solution was to override the `setShowsCancelButton:animated:` method in my `UISearchBar` subclass to call `[super setShowsCancelButton:NO animated:animated];`. This allowed the cancel button to remain hidden and resolved my missing caret problem. – beebcon Oct 15 '15 at 19:13
15

I had the same issue, but fixed it a different way.

For those who can't or don't want to subclass UISearchDisplayController, I fixed the issue by adding a listener on UIKeyboardWillShowNotification, and setting [self setShowsCancelButton:NO animated:NO] there.

In viewWillAppear::

// Add keyboard observer:
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillAppear:)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

Then you create:

- (void)keyboardWillAppear:(NSNotification *)notification
{
    [YOUR-SEARCHBAR-HERE setShowsCancelButton:NO animated:NO];
}

Don't forget to add,

[[NSNotificationCenter defaultCenter] removeObserver:self];

in viewWillDisappear:!

Hope this helps!

BFeher
  • 557
  • 5
  • 12
7

Similar to Nimrod's answer, you can also subclass UISearchDisplayController and implement the setActive:animated: method:

- (void)setActive:(BOOL)visible animated:(BOOL)animated {
    [super setActive:visible animated:animated];
    self.searchBar.showsCancelButton = NO;
}
biegleux
  • 13,179
  • 11
  • 45
  • 52
quantumkid
  • 438
  • 4
  • 7
6

This seems to be a bug within Xcode. I submitted this error to Apple's bug reporting site, and they've followed up asking for more sample code and use-cases.

Thanks everyone for your attempt at solving this problem.

ArtSabintsev
  • 5,170
  • 10
  • 41
  • 71
  • 2
    I am having the same issue, but in reverse. I have tried to create the UISearchDisplayController and UISearchBar using a nib file and programmatically, but instead of hiding the cancel button, I need it to be shown all the time. In the controller, I have the view set the button to be visible, as well as in a few of the delegate methods, but when I cancel the search, the button disappears, then reappears. Hope this get's sorted soon. – Byron Rode Jan 23 '12 at 19:36
  • 2
    They responded saying it's a known issue and they're trying to fix it. – ArtSabintsev Jan 23 '12 at 21:06
  • Glad they are aware of it, makes my search bar look a bit crap. Need to use a custom button in the meantime. – Byron Rode Jan 23 '12 at 21:39
3
class CustomSearchBar: UISearchBar {

    override func setShowsCancelButton(showsCancelButton: Bool, animated: Bool) {
        super.setShowsCancelButton(false, animated: false)
    }

}

class CustomSearchController: UISearchController, UISearchBarDelegate {

    lazy var _searchBar: CustomSearchBar = {
        [unowned self] in
        let customSearchBar = CustomSearchBar(frame: CGRectZero)
        customSearchBar.delegate = self
        return customSearchBar
    }()

    override var searchBar: UISearchBar {
        get {
            return _searchBar
        }
    }

}
Elijah
  • 8,381
  • 2
  • 55
  • 49
3

Had this problem when using the UISearchBar with UISearchController. I'm using my own cancel button, as the cancel button wasn't showing on iPad with showsCancelButton = YES, now it won't hide on iPhone with showsCancelButton = NO!

The following worked for me.

Set the delegate, and initial value:

- (void)viewDidLoad
{
    // ... 
    self.searchController.searchBar.showsCancelButton = NO;
    self.searchController.searchBar.delegate = self;
}

Reset showsCancelButton to NO 0.1s after the text bar begins editing.

#pragma mark - UISearchBarDelegate
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        self.searchController.searchBar.showsCancelButton = NO;
    });
}
Community
  • 1
  • 1
William Denniss
  • 16,089
  • 7
  • 81
  • 124
2

If you want to avoid the subclassing, implement

searchController.searchBar.showsCancelButton = false;

in these two delegate methods (Do not forget to assign delegates):

- (void)updateSearchResultsForSearchController:(UISearchController *)searchController

- (void)didPresentSearchController:(UISearchController *)searchController

The first one is called everytime you update the searchBar (Cancel button is visible by default) and the second one is for the first searchBar activation.

Jakub Truhlář
  • 20,070
  • 9
  • 74
  • 84
0

If the cancel button shows up when editing the search field of the search bar you could do the following; subclass the search bar and have it implement the UITextFieldDelegateprotocol:

@interface CustomAlignedSearchBar : UISearchBar<UITextFieldDelegate>

Then implement textFieldDidBeginEditing: and do something like:

- (void)textFieldDidBeginEditing:(UITextField *)textField{
    [self setShowsCancelButton:self.cancelButtonShown animated:NO];
}

This will make sure that the cancel button will not show up.

Don Miguel
  • 882
  • 9
  • 19
  • `MLUISearchBar.m:25:37: Property 'cancelButtonShown' not found on object of type 'MLUISearchBar *'` build error – Gank Jan 09 '15 at 09:05
0

After UISearchDisplayController deprecated in iOS8, Apple give handle search presentation to UISearchControllerDelegate.

so you can override searchBar to hide the Cancel button, like below :

- (void)didPresentSearchController:(UISearchController *)searchController {
    [searchController.searchBar setShowsCancelButton:NO];
}

if you need hidden Cancel button from inactive state, you need set searchBar on init :

search = [[UISearchController alloc] initWithSearchResultsController:nil];
[search.searchBar setShowsCancelButton:NO];
ihsan_husnul
  • 125
  • 1
  • 2
0

On iOS 13.0 and later, UISearchController has this property you can use:

@property (nonatomic) BOOL automaticallyShowsCancelButton API_AVAILABLE(ios(13.0)); // Default YES
Julius
  • 1,005
  • 1
  • 9
  • 19
0

Just based on issues I've had before have you tried setting it in:

- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller

I don't know how to ask this question in your question sorry if this is out of place.

jamesC
  • 422
  • 6
  • 25
  • so you need to configure the cancel button when you create the UISearchDisplayController, not wait until it has begun a search. – jamesC Dec 18 '11 at 05:44