UISearchDisplayController with displaysSearchBarIn

2020-05-19 00:39发布

问题:

I am using a UISearchDisplayController with the new ios 7 feature displaysSearchBarInNavigationBar and opaque navigation bars. The search display controller seems to position it's view incorrectly.

I tried plugging in to delegate methods and repositioning but I can not get the initial position correct, nor when rotating. In addition, this seems like a sloppy solution.

回答1:

Just enabled "Under Opaque Bars " in the storyboard for your view controller or if you like to code.Then add the below lines.Your good :)

self.edgesForExtendedLayout = UIRectEdgeAll;
self.extendedLayoutIncludesOpaqueBars = YES;


回答2:

I had the same issue and I've fixed in this way:

  1. Subclass UISearchDisplayController to have the UISearchBar in the NavigationBar for iOS 6 and 7. I've overwrite:

    -(void)setActive:(BOOL)visible animated:(BOOL)animated
    {
        if (SYSTEM_VERSION_LESS_THAN(@"7")) {
            if(self.active == visible) return;

            [self.searchContentsController.navigationController setNavigationBarHidden:NO animated:NO];

            if (visible) {
                [self.searchBar becomeFirstResponder];
            } else {
                [self.searchBar resignFirstResponder];
            }
        } else {
            self.searchContentsController.view.frame = CGRectMake(0, 0, kCurrentScreenWidth, kCurrentScreenHeight);
            [super setActive:visible animated:animated];
        }
    }

2.In the UISearchDisplayDelegate I've added this:


    - (void) searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView {
        // iOS7 Hack
        if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7")) {
            controller.searchResultsTableView.contentInset = UIEdgeInsetsMake(0.f, 0.f, 0.f, 0.f);
        }

    }

    - (BOOL) searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
    {
        // -- iOS 7 Hack

        if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7")) {
            controller.searchResultsTableView.frame = CGRectMake(0, 64, kCurrentScreenWidth, kCurrentScreenHeight-64);
            [controller.searchContentsController.view setNeedsLayout];
        }
    }



回答3:

I had the same issue and after hours of searching for an answer, I decided to look through the view hierarchy. It seems that the superview of the searchBar, which is also the dimmed view, has a y origin of 64 and a height of 504, which is not populating the whole screen. Not sure why it's like that. Nevertheless, I ended up setting the y to 0 and it's height to the screen height. After which, I set its y back to the original value, or else your content table view will be distorted. It's not the best solution, but it's better than nothing. Hope this helps you.

- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
    self.savedSearchTerm = nil;
    UIView *dimmedView = controller.searchBar.superview;
    CGRect frame = dimmedView.frame;
    frame.origin.y = 64;
    dimmedView.frame = frame;
    [self.tableView reloadData];
}

- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
    UIView *dimmedView = controller.searchBar.superview;
    CGRect frame = dimmedView.frame;
    frame.origin.y = 0;
    frame.size.height = 568;
    dimmedView.frame = frame;
}


回答4:

I've search endlessly online for a solution to this problem, but nothing that was recommended worked in my case. Resetting the searchResultsTable's frame didn't work because it's origin.y was already at 0. Changing the contentInset kind of worked, but didn't fix the dimmed overlay view and caused issues with the table's scroll view at the bottom (and bars). I did finally get a better working hack, although it's not completely ideal since the view's frame shifts are noticeable briefly, but at least positioning is correct after that.

Using Reveal.app, I was able to dig into the view hierarchy of UISearchDisplayController to figure out what was going on, and this was the result in my case:

UISearchDisplayControllerContainerView
    - UIView (0,0,320,504)
        - UISearchResultsTableView
    - UIView (0,20,320,44)
    - UIView (0,64,320,440)
        - _UISearchDisplayControllerDimmingView

I do everything programmatically, so not sure about a workaround for NIBs. Here are the basics of how my UISearchBar and UISearchDisplayController are setup in my viewDidLoad method:

UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 0)];
searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
searchBar.delegate = self;
[searchBar sizeToFit];

self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
self.searchController.delegate = self;
self.searchController.searchResultsDataSource = self.searchDataSource;
self.searchController.searchResultsDelegate = self;
if ([self.searchController respondsToSelector:@selector(displaysSearchBarInNavigationBar)]) {
    self.searchController.displaysSearchBarInNavigationBar = YES;
}

And my hack that worked in this case:

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

    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fixSearchControllerPositionOnKeyboardAppear)
                                                     name:UIKeyboardWillShowNotification object:nil];

        if (self.searchController.isActive) {
            // the following is needed if you are return to this controller after dismissing the child controller displayed after selecting one of the search results
            [self performSelector:@selector(fixSearchControllerPositionForiOS7) withObject:nil afterDelay:0];
        }
    }
}

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

    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}

- (void)fixSearchControllerPositionForiOS7 {
    UIView *view = self.searchController.searchResultsTableView.superview;
    // only perform hack if the searchResultsTableView has been added to the view hierarchy
    if (view) {

        // The searchDisplayController's container view is already at 0,0, but the table view if shifted down 64px due to
        // bugs with the subviews in iOS 7, so shift the container back up by that negative offset.
        // This also fixes the position of the dimmed overlay view that appears before results are returned.
        CGFloat yOffset = 64.0;
        CGRect viewFrame = view.frame;
        if (CGRectGetMinY(viewFrame) == 0) {
            viewFrame.origin.y = -yOffset;
            viewFrame.size.height += yOffset;
            [UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
                view.frame = viewFrame;
            } completion:nil];
        }

        // we also need to adjust dimmed overlay view, so iterate through the search view controller's container
        // view and make sure all subviews have their vertical origin set to 0
        UIView *searchContainerView = view.superview;
        for (NSInteger i = 0; i < [searchContainerView.subviews count]; i++) {
            UIView *subview = searchContainerView.subviews[i];
            if (CGRectGetMinY(subview.frame) > 0) {
                CGRect subviewFrame = subview.frame;
                CGFloat offset = CGRectGetMinY(subviewFrame);
                subviewFrame.origin.y = 0;

                if (offset == 20.0) {
                    // this subview is partially responsible for the table offset and overlays the top table rows, so set it's height to 0
                    subviewFrame.size.height = 0;
                }
                else {
                    // this subview is the dimmed overlay view, so increase it's height by it's original origin.y so it fills the view
                    subviewFrame.size.height += offset;
                }
                subview.frame = subviewFrame;
            }
        }
    }
}

- (void)fixSearchControllerPositionOnKeyboardAppear {
    // call hack to reset position after a slight delay to avoid UISearchDisplayController from overriding our layout fixes
    [self performSelector:@selector(fixSearchControllerPositionForiOS7) withObject:nil afterDelay:0.1];
}

- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
        [self fixSearchControllerPositionForiOS7];
    }
}

I had to add an observer for when the keyboard appears as this was causing the UISearchDisplayController to re-layout its subviews, along with a short delay to ensure my position adjustments were applied after UISearchDisplayController did it's layout stuff.



回答5:

1.Use Reveal, find cover layer and found to be _UISearchDisplayControllerDimmingView

2.Find the layer , modify the corresponding frame, it can be found on the map by dimmingview with searchResultsTableView sub- layer of the same view.

- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
for(UIView * v in controller.searchResultsTableView.superview.subviews)
{
    if([v isKindOfClass:[NSClassFromString(@"_UISearchDisplayControllerDimmingView") class]])
    {
        v.frame = CGRectMake(0,20,320,400); // modify the frame
        NSLog(@"--------- %@",[v class]);
    }
}

3.Likewise, if you need to adjust searchResultsTableView the frame, append the following code in the

- (void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView
{
    tableView.frame =CGRectMake(0, 20, 320, 480-64-44);
}