Show a preloaded search results?
Asked Answered
F

7

12

I have a non-tableview view with a searchbar in it, and while it works perfectly, the search display controller hides the table view and overlays a dark dimmed view when an empty string is in the searchbar. I want it to show a preloaded data when the empty string is in the searchbar instead of hiding the table view and overlaying the dark dimmed view underneath the searchbar. Just like how the Google search bar in Safari for iOS works.

I found a similar question asked on stackoverflow before: UISearchDisplayController - how to preload searchResultTableView, I couldn't really get it to work.

I have no problem getting the preloaded data and setting the current data to it, but I'm not sure how to prevent the displaycontroller from removing the searchResultsTableView.

Thanks in advance.

Fabien answered 24/5, 2012 at 9:27 Comment(0)
F
13

I finally found a way to do this.

I found out that the searchDisplayController simply removes the searchResultsTableView from the superview, so I just added the table view back into the superview whenever the display controller tried to hide the table view:

- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
    // add the tableview back in
    [self.view addSubview:self.searchDisplayController.searchResultsTableView];
}

and then I also have to show the tableview the first time the searchbar is clicked, so I did:

- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
    // after the data has been preloaded
    self.searchResults = self.allItems;
    [self.searchDisplayController.searchResultsTableView reloadData];
}

- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
    [self.view addSubview:self.searchDisplayController.searchResultsTableView];
}

For me, 'allItems' is where I stored all the searchable items and 'searchResults' is where the filtered items (after the search) is stored. And of course, you would have to preload the items (e.g. search history) before reloading the data.

I don't know if this is a nice way or not to do it in terms of the performance and what not, but it worked perfectly for me, and I hope this could be useful for other people as well. Please comment if there is a better way to do this.

Fabien answered 28/5, 2012 at 4:43 Comment(4)
Been trying to figure this out for days now! Thanks for the solution!Almeta
After testing this implementation I now want to figure out how to get the search results container to not flicker when it is added to the view. Since it is added via the searchDisplayControllerDidBeginSearch: method, you can see the dim background for a split second before the searchResultsTableView is shown. Any thoughts on how to get around this? I tried moving the line of code in the searchDisplayControllerDidBeginSearch to the searchDisplayControllerWillBeginSearch' method, but that causes the dim background to be placed in front of the searchResultsTableView`Almeta
This is no longer working on iOS 7. Anyone have a working solution?Crescint
Yeah this doesn't work in iOS 8. Anyone have a solution?Unscratched
O
4

After hours and hours I finally figured out a solution that works in iOS 7

Just implement the following two methods in your UISearchDisplayDelegate

-(void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView {

    // We need to prevent the resultsTable from hiding if the search is still active
    if (self.searchDisplayController.active == YES) {
        tableView.hidden = NO;
    }
}

When the search starts, the searchResultsTableView is being hidden automatically, so we need to unhide it again

- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {

    controller.searchResultsTableView.hidden = NO;

    // Then we need to remove the semi transparent overlay which is here
    for (UIView *v in [[[controller.searchResultsTableView superview] superview] subviews]) {

        if (v.frame.origin.y == 64) {
            [v setHidden:YES];
        }
    }

}
Oxide answered 16/2, 2014 at 23:24 Comment(1)
This did not work on iOS7 for me. Instead, I checked if (v.alpha < 1), seems like the most "sustainable" hack.Calamondin
D
4

I found a much better solution to this issue, and it seems to work perfectly on iOS 6 and 7. While it is still a hack, its a much cleaner and future proof hack than the above. The other solutions do not work consistently and prevent some UISearchDisplayDelegate methods from ever firing! Further I had complex insetting issues which I could not resolve with the above methods. The main issue with the other solutions is that they seriously confuse the internals of the UISearchDisplayController. My solution is based on the observation that UISearchDisplayContoller is a UISearchbarDelegate and that the automatic undimming & showing of results table can be triggered by simulating a keypress in the search field! So:

- (void) searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller 
{
    if ([controller respondsToSelector: @selector(searchBar:textDidChange:)])
        [(id<UISearchBarDelegate>)controller searchBar: controller.searchBar textDidChange: @" "];
}

This code is future proof against crashing by checking it responds to the UISearchbarDelegate method, and sends space @" " to trick the UISearchDisplayController into thinking user has typed a letter.

Now if the user types something and then erases it, the table will dim again. The other solutions try to work around this by doing something in the searchDisplayController:didHideSearchResultsTableView: method. But this doesn't make sense to me, as surely when you cancel the search it will need to truly hide your results table and you may need to run code in this case. My solution for this part is to subclass (note you could probably use a Method Swizzled Category to make it work everywhere if needed in your project):

// privately declare protocol to suppress compiler warning
@interface UISearchDisplayController (Super) <UISearchBarDelegate>
@end

// subclass to change behavior
@interface GMSearchDisplayController : UISearchDisplayController
@end

@implementation GMSearchDisplayController

- (void) searchBar: (UISearchBar *) searchBar textDidChange: (NSString *) searchString
{
    if (searchString.length == 0)
        searchString = @" ";
    if ([super respondsToSelector: @selector(searchBar:textDidChange:)])
        [super searchBar: searchBar textDidChange: searchString];
}

@end

This code works by intercepting the textDidChange delegate method and changing nil or empty strings in to space string @" " preventing the normal hiding/dimming that occurs on an empty search bar. If you are using this second bit of code, then you could modify the first bit to pass a nil instead of @" " as this second bit will do the needed conversion to @" " for you.

In my own project, I needed to handle the case that user does type a space, so instead of @" " above I used a defined token:

// arbitrary token used internally
#define SEARCH_PRELOAD_CONDITIONAL @"_#preresults#_"

And then handle it internally by converting it back to nil string:

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
    if ([searchString isEqualToString: SEARCH_PRELOAD_CONDITIONAL])
        searchString = nil;
}

Enjoy! :)

Debarath answered 17/7, 2014 at 12:5 Comment(0)
A
2

This works in iOS 8:

- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
  self.searchDisplayController.searchResultsTableView.hidden = NO;
}

- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
    self.searchDisplayController.searchResultsTableView.hidden = NO;
    [self.searchDisplayController.searchResultsTableView.superview.superview bringSubviewToFront:self.searchDisplayController.searchResultsTableView.superview];

    CGRect frame = self.searchDisplayController.searchResultsTableView.frame;
    self.searchDisplayController.searchResultsTableView.frame = CGRectMake(frame.origin.x, 64, frame.size.width, frame.size.height);
}
Ashburn answered 7/1, 2015 at 2:18 Comment(0)
U
1

When you start searching this method gets called. Add the searchResultsTableView and unhide it. It would then display your already preloaded data. I must have your data preloaded in order for this to work.

- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
    CGRect testFrame = CGRectMake(0, self.notesSearchBar.frame.size.height, self.notesSearchBar.frame.size.width, self.view.frame.size.height - self.notesSearchBar.frame.size.height);
    self.searchDisplayController.searchResultsTableView.frame = testFrame;
    [self.notesSearchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];

//    [self.view addSubview:self.searchDisplayController.searchResultsTableView];
    controller.searchResultsTableView.hidden = NO;
}

-(void) searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
    CGRect testFrame = CGRectMake(0, self.notesSearchBar.frame.size.height, self.notesSearchBar.frame.size.width, self.view.frame.size.height - self.notesSearchBar.frame.size.height);
    self.searchDisplayController.searchResultsTableView.frame = testFrame;
    [self.notesSearchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];

    //    [self.view addSubview:self.searchDisplayController.searchResultsTableView];
    controller.searchResultsTableView.hidden = NO;
}


-(void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
    controller.searchResultsTableView.hidden = YES;
}
Unscratched answered 30/10, 2013 at 2:22 Comment(0)
M
1

iOS 9 working code.

- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
    // Bring the search table view to the view's front
    self.searchDisplayController.searchResultsTableView.hidden = NO;
    [self.searchDisplayController.searchResultsTableView.superview bringSubviewToFront:self.searchDisplayController.searchResultsTableView];
}

- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView {
    // We need to prevent the resultsTable from hiding if the search is still active
    if (self.searchDisplayController.active == YES) {
        tableView.hidden = NO;
    }
}
Malachi answered 7/1, 2016 at 14:24 Comment(0)
M
0

Swift 2.0+ version

func searchDisplayControllerDidBeginSearch(controller: UISearchDisplayController) {
    controller.searchResultsTableView.hidden = false
    controller.searchResultsTableView.superview!.bringSubviewToFront(controller.searchResultsTableView)
}

func searchDisplayController(controller: UISearchDisplayController, didHideSearchResultsTableView tableView: UITableView) {
    if ((searchDisplayController?.active) != nil) {
        tableView.hidden = false
    }
}
Mcdougald answered 9/3, 2016 at 15:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.