MLQ
MLQ

Reputation: 13511

UISearchBar does not display Cancel button when added as subview to UINavigationBar

I'm following the answer at https://stackoverflow.com/a/14235624/855680 to animate my UISearchBar which starts off with a smaller width, then expands to the full width of the iPhone screen when active. It expands as expected, except that the Cancel button does not appear at all. I've tried calling setShowsCancelButton:animated in both searchBarTextDidBeginEditing: and searchDisplayControllerWillBeginSearch:, but to no avail. What am I missing? Here's my code:

HomeViewController.h

#import <UIKit/UIKit.h>
@interface HomeViewController : UIViewController <UISearchBarDelegate, UISearchDisplayDelegate>
@end

HomeViewController.m

#import "HomeViewController.h"

@interface HomeViewController ()

@property (strong, nonatomic) UISearchDisplayController *sdc;
@property (strong, nonatomic) UISearchBar *searchBar;

@end

@implementation HomeViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Add dummy buttons to navigation bar.
    UIBarButtonItem *btn1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:nil];
    UIBarButtonItem *btn2 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:nil];
    [self.navigationItem setLeftBarButtonItems:@[btn1, btn2] animated:YES];

    // Add UISearchBar.
    self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(100, 0, 150, 44)];
    self.sdc = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
    self.sdc.delegate = self;
    [self.navigationController.navigationBar addSubview:self.searchBar];
}

// From this point onwards, pretty much copy-paste from the StackOverflow answer.
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self selector:@selector(adjustFrame:) name:UIKeyboardWillShowNotification object:nil];
    [nc addObserver:self selector:@selector(adjustFrame:) name:UIKeyboardWillHideNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [nc removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

- (void)adjustFrame:(NSNotification *) notification {
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3];
    [UIView setAnimationBeginsFromCurrentState:YES];

    if ([[notification name] isEqual:UIKeyboardWillHideNotification]) {
        // revert back to the normal state.
        self.searchBar.frame = CGRectMake (100, 0, 150, 44);

    }
    else  {
        //resize search bar
        self.searchBar.frame = CGRectMake (0,0,320,self.searchBar.frame.size.height);
    }

    [UIView commitAnimations];
}

// Try to catch the editing event and display the Cancel button.
// BOTH DON'T WORK.
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
    [searchBar setShowsCancelButton:YES animated:YES];
}

- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
    [controller.searchBar setShowsCancelButton:YES animated:YES];
}

@end

Upvotes: 2

Views: 5105

Answers (4)

Jirune
Jirune

Reputation: 2340

Here is the simplest solution for iOS7.

- (void) viewDidLoad
{
     self.navigationItem.leftBarButtonItem = LEFT_ITEM;
     self.navigationItem.rightBarButtonItem = SEARCH_ICON;
}

- (void)searchIconPressed
{
     self.navigationItem.leftBarButtonItem = nil;
     self.navigationItem.rightBarButtonItem = nil;

    self.navigationItem.titleView = self.searchBar;

    UIBarButtonItem* cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelSearch)];
    [self.navigationItem setRightBarButtonItem:cancel animated:YES];

    [self.searchBar becomeFirstResponder];
}

Now you will have a search bar with Cancel button also.

Upvotes: 0

Paulo
Paulo

Reputation: 1245

Is there any reason why you are putting the search bar in the navigation bar? I think your code should work if your were placing the search bar somewhere in the view, the navigation bar is tricky if you wish to put something in it you generally define views or items to replace the objects in your navbar like the rightbarbuttonitem / leftbarbuttonitem or title

    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:infoButton];

Upvotes: 0

MLQ
MLQ

Reputation: 13511

Figured this out myself!

I tried programmatically creating the UISearchBar and the UISearchDisplayController in a new project, but instead of adding the search bar in the navigation bar, I added it to the main view of a view controller. It worked that way, the Cancel button shows whenever I click on the search bar, except that it doesn't resize back to the original frame when I stop editing--but that's for another discussion. Afterwards, I went back to this project and printed out self.searchBar.showsCancelButton after every line of code where I set it to YES, and it turned out the value is, in fact, YES. So it is the UINavigationBar that, for some reason, does not show the UISearchBar's Cancel button. My solution, then, was to create a fake "Cancel" button in the navigation bar's rightBarButtonItem.

On start, the navigation bar looks like this:

enter image description here

Then, when I click on the search bar, I expand it to a width that's just enough to cover the two left bar button items, but leave some space to keep the right bar button item visible. Then, that right bar button item serves as the Cancel button (I just used a system "Add" button for demo's sake).

enter image description here

When I click on "Search" in the keyboard, or on the plus button, the search bar reverts to its old size and the right bar button item disappears. My full code is below:

HomeViewController.h

#import <UIKit/UIKit.h>
@interface HomeViewController : UIViewController <UISearchBarDelegate>
@end

HomeViewController.m

#import "HomeViewController.h"

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

@implementation HomeViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Just some buttons that the search bar will overlap when active.
    UIBarButtonItem *btn1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:nil];
    UIBarButtonItem *btn2 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:nil];
    [self.navigationItem setLeftBarButtonItems:@[btn1, btn2] animated:YES];

    self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(100, 0, 150, 44)];    self.searchBar.delegate = self;
    [self.navigationController.navigationBar addSubview:self.searchBar];
}

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
    // Set a fake Cancel button.
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(stopEditing)];

    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^(){
        self.searchBar.frame = CGRectMake(0, 0, 280, 44);
    } completion:nil];

    // Bring search bar to the front because adding a right bar button
    // item somehow puts it behind the UIBarButtonItems.
    [self.navigationController.navigationBar bringSubviewToFront:self.searchBar];
    return YES;
}

- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar {

    // Go back to the old frame.
    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^(){
        self.searchBar.frame = CGRectMake(100, 0, 150, 44);
    } completion:nil];

    // Remove the "Cancel" button.
    self.navigationItem.rightBarButtonItem = nil;

    [self.navigationController.navigationBar bringSubviewToFront:self.searchBar];

    return YES;
}

- (void)stopEditing {
    [self.searchBar resignFirstResponder];
}

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
    [self stopEditing];
}

@end

Upvotes: 2

SukruK
SukruK

Reputation: 560

You should set delegate of UISearchBar self.searchBar.delegate = self;

- (void)viewDidLoad {
[super viewDidLoad];

// Add dummy buttons to navigation bar.
UIBarButtonItem *btn1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:nil];
UIBarButtonItem *btn2 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:nil];
[self.navigationItem setLeftBarButtonItems:@[btn1, btn2] animated:YES];

// Add UISearchBar.
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(100, 0, 150, 44)];
self.sdc = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.sdc.delegate = self; 

self.searchBar.delegate = self;

[self.navigationController.navigationBar addSubview:self.searchBar];

}

Upvotes: 1

Related Questions