Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 7 UISearchDisplayController Animation Glitches

I'm trying to add searching to my UITableViewController via UISearchDisplayController, however I'm seeing a very weird animation glitch when searching begins and ends.

On iPhone, the animation into the navigation bar works fine. However, when ending search, the navigation bar lags behind the search bar while animating down. This causes a white strip to show between the navigation bar and the search bar. On iPad, the animations are completely messed up.

iPhone video

and

iPad video

As the above videos show, the stock apps do not suffer from these animation glitches. Does anybody have any ideas what is causing the issues?

I'm creating the UISearchDisplayController with the following inside the "Master" view controller.

- (void)viewDidLoad
{
   [super viewDidLoad];

   UISearchBar* searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)];
   searchBar.delegate = self;
   self.tableView.tableHeaderView = searchBar;

   self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
   self.searchController.delegate = self;
   self.searchController.searchResultsDataSource = self;
   self.searchController.searchResultsDelegate = self;
}

I also tried doing this using Storyboards but the same animation glitches occur.

like image 459
Rick Avatar asked Apr 26 '14 02:04

Rick


2 Answers

Solution for non-translucent navbar (less code, less universality)

The white glitch you see is a tableview's background color which is visible through a gap between navbar and searchbar. The gap is definitely Apple developer's oversight.

So the solution looks like this:

- (void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
    UIView *topTableViewBG = [[UIView alloc] initWithFrame:CGRectMake(0, -64, CGRectGetWidth(self.tableView.bounds), 64)];
    topTableViewBG.backgroundColor = self.navigationController.navigationBar.backgroundColor;
    topTableViewBG.tag = 1234567;
    [self.tableView insertSubview:topTableViewBG belowSubview:self.tableView.tableHeaderView];
}
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{   
    [[self.tableView viewWithTag:1234567] removeFromSuperview];
}

here I add a custom view right under the search bar. It happens just before transition to normal VC. The view is positioned at the special point so it covers the gap between the navbar and the searchbar. After the transition is finished, I remove the custom view.

UPDATE: Universal solution (more code, more universality)

the solution above is good only if there is non-translucent navbar. For translucent navbar there is a challange to find correct color for our 'gap-stopper' view. But as soon we are pretty free to change searchbar color, we can use searchbar color for gap-stopper.

Lets make some changes to our code

First, we need non-translucent searchbar, so set background image to search bar:

UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
[searchBar setBackgroundImage:[UIImage pixelImageWithColor:SEARCHBAR_GRAY_COLOR] forBarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault];
searchBar.delegate = self;
self.tableView.tableHeaderView = searchBar;

here is the UIImage category method, that was used above:

+ (UIImage *)pixelImageWithColor:(UIColor *)color {
    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB ();
    CGContextRef ctx = CGBitmapContextCreate (NULL, 1, 1, 8, 0, cs, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault);
    CGColorSpaceRelease (cs);
    
    CGContextSetFillColorWithColor (ctx, color.CGColor);
    CGContextFillRect (ctx, CGRectMake (0.0f, 0.0f, 1.0f, 1.0f));
    CGImageRef cgImage = CGBitmapContextCreateImage (ctx);
    CGContextRelease (ctx);
    
    UIImage *result = [UIImage imageWithCGImage:cgImage];
    CGImageRelease (cgImage);
    return result;
}

Then change our searchDisplayControllerWillEndSearch method:

- (void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
    UIView *topTableViewBG = [[UIView alloc] initWithFrame:CGRectMake(0, -64, CGRectGetWidth(self.tableView.bounds), 64)];
    topTableViewBG.backgroundColor = SEARCHBAR_GRAY_COLOR;
    topTableViewBG.tag = 1234567;
    [self.tableView insertSubview:topTableViewBG belowSubview:self.tableView.tableHeaderView];
}

and finally the searchDisplayControllerDidEndSearch method remains unchanged:

- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{   
    [[self.tableView viewWithTag:1234567] removeFromSuperview];
}

Honestly this solution is more universal and it looks much prettier than one I've described in first part of the answer.

like image 134
kas-kad Avatar answered Sep 28 '22 08:09

kas-kad


I find the best solution for me, it's looks exactly as I need, I'm using swift but if you need you can easily translate it to objective-c

self.edgesForExtendedLayout = .None

let barBacgroundView = UIView(frame: CGRectMake(0, 0, self.view.frame.size.width, 64))
barBacgroundView.backgroundColor = self.tableView.backgroundColor
UIApplication.sharedApplication().delegate!.window!!.insertSubview(barBacgroundView, atIndex: 0)

Insert this code in viewDidLoad() and that's it. Hope this helps :)

like image 29
vsilux Avatar answered Sep 28 '22 09:09

vsilux