I'm trying to reproduce a search interface similar to the one of the Maps app.
I'm using a UISearchController
with its searchBar
inside my navigationBar
. I also have a rightBarButtonItem
on that navigation bar.
By default, when presenting the search controller, the cancel button of the search bar shows next to the existing right item. What I want to do — like with the Maps app — is to hide my right navigation item while the searchController is presented and show it again when it's dismissed.
I have managed to do it using the delegates methods, but there is a jump in the animation which is very ugly and I would like to know how to avoid that.
Here is my code (the right bar item will toggle slow animations which makes the bug easier to see)
AppDelegate.h
//
// AppDelegate.h
// SearchNavigationItemAnimation
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
AppDelegate.m
//
// AppDelegate.m
// SearchNavigationItemAnimation
//
#import "AppDelegate.h"
#import "SearchViewController.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
SearchViewController *rootController = [[SearchViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:rootController];
self.window.rootViewController = navigationController;
[self.window makeKeyAndVisible];
return YES;
}
@end
SearchViewController.h
//
// SearchViewController.h
// SearchNavigationItemAnimation
//
#import <UIKit/UIKit.h>
@interface SearchViewController : UIViewController
@end
SearchViewController.m
//
// SearchViewController.m
// SearchNavigationItemAnimation
//
#import "SearchViewController.h"
@interface SearchViewController () <UISearchControllerDelegate, UISearchResultsUpdating>
@property (nonatomic, strong) UISearchController *searchController;
@property (nonatomic, strong) UIBarButtonItem *rightBarButtonItem;
@end
@implementation SearchViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor lightGrayColor];
self.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:@selector(navigationBarButtonItemAction)];
UITableViewController *searchResultsController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
self.searchController.delegate = self;
self.searchController.searchResultsUpdater = self;
self.searchController.hidesNavigationBarDuringPresentation = NO;
self.navigationItem.titleView = self.searchController.searchBar;
self.navigationItem.rightBarButtonItem = self.rightBarButtonItem;
self.definesPresentationContext = YES;
}
- (void)willPresentSearchController:(UISearchController *)searchController
{
[self.navigationItem setRightBarButtonItem:nil animated:true];
}
- (void)willDismissSearchController:(UISearchController *)searchController
{
[self.navigationItem setRightBarButtonItem:self.rightBarButtonItem animated:true];
}
- (void)navigationBarButtonItemAction {
float windowLayerSpeed = [UIApplication sharedApplication].keyWindow.layer.speed;
[UIApplication sharedApplication].keyWindow.layer.speed = (windowLayerSpeed == 1.0) ? 0.1 : 1.0;
}
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
}
@end
EDIT
Let me explain a little bit more what is going on.
With no added barButtonItem, the searchBar takes the entire width of the navigation bar. When you touch it, it activates and shows the cancel button with a nice animation (the search field shrinks leaving room for the cancel button which slides in from the right).
With a right button on the navigation bar, if you don't try to add it/remove it/modify it in any way, it works the same way. The search bar takes all the space except for the width of the button and when it actives the cancel button slides in from the right and from below the button and lands next to it (between the search field and the existing right button). Everything is animated and works fine.
Now if you want to make the right button disappear when the cancel button slides in you have to animate it's frame. The problem is that the search bar automatically try to take all the available space, and does that without animating it's frame. So lets' say you want to animate your button's frame width to zero, the search bar will expand its width instantly instead of following your animation.
It's clearly a bug, or if you're more forgiving a feature that hasn't been implemented... But it worked fine in iOS7 with UISearchDisplayController
, and Apple does it in the Maps app. So I'm asking for any workaround that will work, including private API call if necessary.