1
votes

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.

1
I would try making a custom view with a UIButton instead of using navigation items. With a button you could set the x position and animate it out of frame. AFAIK, navigation items can only be added and removed. You get no control of position, size, color, etc.Russell Austin
Of course, a quick search turns this up. stackoverflow.com/questions/6136067/… Maybe that would work for you.Russell Austin
Yes but it wouldn't work because I need the search bar to animate along with it. The jump in the animation is caused by the search bar not animating properly when the UIBarButtonItem is added or removed. Animating a custom view is not going to change that.deadbeef
Instead of making the button disappear, have it slide off to the left at the same rate the search bar is growing. After the animation completes, hide the button.Russell Austin
Not going to work either. The problem is that the search bar doesn't animate it's frame change. I can animate my button all I want, if the search bar doesn't animate along it creates the jump that I'm talking about.deadbeef

1 Answers

0
votes

In case anyone is interested, I managed to make it work using UISearchDisplayController. See this sample project.

I'm still interested by a solution that works with UISearchController though.