20

In an app that's supposed to run on iOS 6 and iOS 7, the cancel button of the search bar embedded in the navigation bar is not shown anymore if the app is run on iOS 7. On iOS 6, it works.

The search bar is in the title view of the navigation bar and the cancel button should be shown if the search bar becomes the first responder:

iOS 7

enter image description here

iOS 6

enter image description here

In an isolated test case, the code is very simple:

@interface MyViewController : UITableViewController<UISearchBarDelegate>

@property (nonatomic) IBOutlet UISearchBar* searchBar;

@end


@implementation MyViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.navigationItem.titleView = self.searchBar;
}

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

@end

Is this a deliberate change in iOS 7 that I missed in the documentation? If yes, what is supposed to be the alternative?

If not, have I made a mistake in my code?

Codo
  • 75,595
  • 17
  • 168
  • 206
  • Have you set the delegate for the UISearchBar? – ppaulojr Sep 28 '13 at 15:25
  • 2
    Yes, I've set the delegate in the Interface Builder and `searchBarTextDidBeginEditing:` is called as expected. It wouldn't work on iOS 6 otherwise. – Codo Sep 28 '13 at 15:26

8 Answers8

14

It looks like you're doing everything correctly, but apparently Apple has changed around some things in iOS 7. According to this SO question in iOS 7 the cancel button doesn't appear on a UISearchBar embedded in a UINavigationBar.

According to the developer documentation, the showsCancelButton property may have a slightly different effect than the setShowsCancelButton:Animated method. Try doing this:

searchBar.showsCancelButton = YES;
[searchBar setShowsCancelButton:YES animated:YES];

I'm not sure if that will have any impact. You could also try placing the code in a different delegate method:

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar; // return NO to not become first responder
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar; // called when text starts editing

You may also want to checkout the iOS 7 changelog. It looks like Apple changed the behavior or a UISearchDisplayController / UISearchBar when added to a UINavigationBar. Take a look at the last bullet point under the UIKit section (although it isn't clear exactly what was changed).


You may also want to try using a UISerachDisplayController. What might be even easier is to embed the UISearchBar in the header of a UITableView.

Community
  • 1
  • 1
Sam Spencer
  • 8,492
  • 12
  • 76
  • 133
  • 1
    "the showsCancelButton property is different from the setShowsCancelButton:Animated method" - where do you read that in the docs? The method is a way to set the property with animation. – Bryan Sep 28 '13 at 15:39
  • Thank you for all the proposed ideas. I cannot confirm that `showsCancelButton` and `setShowsCancelButton:animated:` are not related. If I use your code, the problem persists on iOS 7 and the animation on iOS 6 no longer works. Moving the code to a different delegate methods didn't have any effect either. The last bullet point in the iOS 7 changelog is an improvement I'm aware of but it doesn't affect my problem as I'm not using `UISearchDisplayController`. – Codo Sep 28 '13 at 15:55
  • Your best hint was the reference to the other SO question. That way, the cancel button appears. However, the search bar style changes and the proper sizing of the search bar will need some additional effort. It indeed looks as if the search bar purposely behaves differently if it is embedded in a navigation bar. It would be interesting to learn the idea behind that change. – Codo Sep 28 '13 at 15:57
  • I'm accepting your answer because it contained the most relevant information. However, I did go a different route: In iOS 7, I now add the search bar into the header of my table view (instead of into the navigation bar). That seems to be the easiest way to make the cancel button visible. I also tried to use the UISearchDisplayController but it had too many problems (e.g. taps are ignored if the table view is not scrolled to the very top). – Codo Oct 04 '13 at 12:48
12

I've solved this problem simple, just by adding rightBarButtonItem :)

self.navigationItem.titleView = self.searchBar;
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Cancel", nil) style:UIBarButtonItemStylePlain target:self action:@selector(didClickCancelButton:)] autorelease];

But sure you have to check if current iOS version is >= 7.0, otherwise you will get two "cancel" buttons..

BTW This method allows you to have "cancel" button which always enabled

Andrey Soloviev
  • 412
  • 3
  • 8
2

iOS 7 is different with iOS 6 in navigation bar, so if you want to show UISearch bar in navigation bar,you can try this:

put your UISearchbar on a UIView like this[self.searchView addSubview self.searchBar], and set the navigationbar's titleView to your searchView like thisself.navagitioncontroller.navigationItem.titleView = self.searchView

Hope it works for you

morisunshine
  • 780
  • 4
  • 7
2

If you are using your UISearchBar with an UISearchDisplayController, you can simply set the cancel button to show, in the "searchDisplayControllerWillBeginSearch" delegate method, like so: (iOS 7 tested)

-(void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller{
    controller.searchBar.showsCancelButton = YES;
}
  • If the UISearchBar is located in the navigation bar, the cancel button isn't shown. The UISearchDisplayController doesn't help here. If the UISearchBar is put in the header of a UITableView and used in combination with UISearchDisplayController, then the cancel button is shown, but it doesn't react to tap events if the UITableView's scroll view isn't positioned at the very top. It's a bit of a mess. – Codo Oct 27 '13 at 09:35
1

There does seem to be a change between iOS6 and iOS7 in that changes to the UI from xxxDidYYY methods sometimes don't work, and you have to do it in the xxxWillYYY method or in some code executed from the main event loop (e.g. in a block or after a short delay).

In your case, try this:

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
    searchBar.showsCancelButton = YES;
    return YES;
}
Bryan
  • 11,398
  • 3
  • 53
  • 78
  • 1
    I've tried your code but it doesn't change anything. Even if I configure the search bar to display the cancel button at all times, it won't show it. So the problem doesn't seem to be related to used delegate methods. – Codo Sep 28 '13 at 15:59
1

In my opinion this is a bug. Heres my workaround. It's not perfect but it works both on iOS 6&7. On iOS7 the searchbar textfield slides over the cancel button while it's fading out and on iOS6 the textfield width expansion isn't animated.

@interface FTViewController ()
@property(nonatomic, strong) UISearchBar *searchBar;
@end

@implementation FTViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.searchBar = [[UISearchBar alloc] init];
    self.searchBar.delegate = self;

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_6_1) {
        // iOS 6.1 and older (only tested on 6.1)
        [self.searchBar sizeToFit];
        self.searchBar.backgroundImage = nil;
    }

    self.navigationItem.titleView = self.searchBar;
}

-(void)cancelBarButtonItemClicked:(id)sender
{
    [self searchBarCancelButtonClicked:self.searchBar];
}

-(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
    [searchBar resignFirstResponder];
    [self.navigationItem setRightBarButtonItem:nil animated:YES];
}

-(BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
    UIBarButtonItem *cancelBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelBarButtonItemClicked:)];
    [self.navigationItem setRightBarButtonItem:cancelBtn animated:YES];

    return YES;
}
@end
MBulli
  • 1,659
  • 2
  • 24
  • 36
1

There are two options:

  1. Add a search bar like a subview to uiviewcontroller.view and hide a navigation bar if needs
  2. Add a cancel button to uiviewcontroller.navigationItem.rightBarButtonItem

my preference is the second one, but it looks more native with the first one.

Evgeny Karpov
  • 2,386
  • 26
  • 16
1

This is indeed a bug that is fixed as of 7.1.

Phil Loden
  • 1,394
  • 1
  • 15
  • 15