iOS 11 SearchBar in NavigationBar

UinavigationbarUisearchbarIos11

Uinavigationbar Problem Overview


With iOS 11 Apple has redesigned the UISearchBar by making the corners rounder and the height bigger. Adding a UISearchBar to the navigationBar is pretty simple by just setting it as the titleView of the navigationItem using navigationItem.titleView = searchBar.

However, in iOS 11 it does not seem to work anymore as expected. Have a look at the screens where we compare the same setup using iOS 10 and iOS 11

iOS 10 enter image description here

iOS 11 enter image description here

You can clearly see that the SearchBar increases the size of the NavigationBar but the bar buttons do not get aligned correctly. Also the searchBar does not use the available space on the left anymore.

Putting the searchBar into a wrapper view to get the cancel button on iPad as described here https://stackoverflow.com/questions/30474494/cancel-button-is-not-shown-in-uisearchbar also does not seem work anymore since the searchBar is then not visible at all.

If anyone has similar issues or already knowns how to fix/improve this I would be very thankful.

This was built using Xcode 9 Beta 4. Maybe future releases will fix this issue.

UPDATE:

Since this does not get fixed we decided to use following solution. We added a new UIBarButtonItem to the NavBar which then presents a new ViewController where we only put a searchBar and nothing else into the NavBar which seems to work. Using the selected answer may be the best solution since Apple with iOS 11 wants us to use this new design even if it does not give us the result we originally wanted. Another way to possible solve this could be a custom SearchBar but this is another topic.

Uinavigationbar Solutions


Solution 1 - Uinavigationbar

There's a new searchController property on navigationItem in iOS 11.

https://developer.apple.com/documentation/uikit/uinavigationitem/2897305-searchcontroller

Use like this...

if #available(iOS 11.0, *) {
     navigationItem.searchController = searchController
} else {
     // Fallback on earlier versions
     navigationItem.titleView = searchController?.searchBar
}

In Objective-C the if statement looks like this:

if (@available(iOS 11.0, *)) {

On iOS 11, if you don't set navigationItem.hidesSearchBarWhenScrolling = false, the search bar may initially be hidden, unless the user scrolls down to reveal it. If you do set it to false, it appears stacked below where the title would go without the user having to scroll.

Solution 2 - Uinavigationbar

You can change the height of UISearchBar in iOS 11 by adding a constraint of height 44:

if #available(iOS 11.0, *) {
    searchBar.heightAnchor.constraint(equalToConstant: 44.0).isActive = true
}

Solution 3 - Uinavigationbar

I was having the same issue and after a few day googling the issue, I found this page - https://translate.google.com/translate?hl=en&sl=zh-CN&u=http://www.jianshu.com/p/262f6e34a7d3&prev=search.

This page leads to this git repo - https://github.com/DreamTravelingLight/searchBarDemo - this demo project shows how to use the old way with titleView to still have a searchBar without the sizing issue.

The key lines are these

_searchBar = [self addSearchBarWithFrame:CGRectMake(0, 0, kScreenWidth - 2 * 44 - 2 * 15, 44)];
UIView *wrapView = [[UIView alloc] initWithFrame:_searchBar.frame];
[wrapView addSubview:_searchBar];
self.navigationItem.titleView = wrapView;

If you embed the UISearchBar inside a view, and set that wrapView as the titleView, the UISearchBar will have the size you set for it, and will fit the nav bar as intended.

Thanks, David

Solution 4 - Uinavigationbar

If you really want to use the native UISearchBar (and avoid the needs of creating your custom components) in the iOS 11+ navigationBar, you could create a container view for that searchBar to have full control over the frame. This container view would be the superview of the searchBar you pass in.

Something like:

class SearchBarContainerView: UIView {

    let searchBar: UISearchBar

    required init?(coder aDecoder: NSCoder) {
        searchBar = UISearchBar()
        super.init(coder: aDecoder)
    }

    init(searchBar: UISearchBar) {
        self.searchBar = searchBar
        super.init(frame: CGRect(x: 0.0, y: 0.0, width: 0.0, height: 44.0))
        addSubview(searchBar)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        searchBar.frame = bounds
    }
}

And then:

let containerView = SearchBarContainerView(searchBar: searchController.searchBar)
containerView.frame.size.width = navigationController?.navigationBar.frame.size.width ?? 0.0
navigationItem.titleView = containerView

> Note that this is just a quick demo and is not ready for navigationBar frame changes (display rotation etc.). You could solve that with e.g. autoresizingMask.

Solution 5 - Uinavigationbar

Make a summary in iOS 14+

First to fix push cause extra height:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    navigationController?.view.setNeedsLayout() // force update layout
    navigationController?.view.layoutIfNeeded() // to fix height of the navigation bar
}

Second to fix pop to origin height:

extendedLayoutIncludesOpaqueBars = true

All above code set in the controller with search bar.

Solution 6 - Uinavigationbar

I think you'll have to deal with setting the new UINavigationItem.searchController property to your UISearchController object. That's how you get the new effect as seen in Messages. It looks like the old behavior is just plain gone. I hope I'm wrong, but the whole API got an overhaul for 11. I know it's buggy in general so we'll see with newer betas and the GM if this gets fixed. (Writing at time of Beta 6)

Solution 7 - Uinavigationbar

This helped me:

    if ([self.navigationItem respondsToSelector:@selector(setSearchController:)])
    {
        [self.navigationItem performSelector:@selector(setSearchController:) withObject:self.searchController];
    }
    else
    {
        self.tableView.tableHeaderView = self.searchController.searchBar;
    }

Solution 8 - Uinavigationbar

I tried out with searchController andsearchBar but found thatsearchBar is meeting my requirements(search on click of the search button).

-(void)searchBarSetUp{
//Search button to reload the radio content.
self.searchButton.action = @selector(addSearchBarToNavigationTitleView);
self.searchButton.target = self;

//search bar initialization
searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
searchBar.delegate = self;
searchBar.showsCancelButton = YES;
[searchBar sizeToFit]; }

-(void)addSearchBarToNavigationTitleView {
// Install the search bar as the table header.
self.navigationItem.titleView = searchBar;}

I too faced difficulties to handle the black screen if I am using searchControl.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPatrickDotStarView Question on Stackoverflow
Solution 1 - UinavigationbarJustin DomnitzView Answer on Stackoverflow
Solution 2 - UinavigationbarzgjieView Answer on Stackoverflow
Solution 3 - UinavigationbarDavid SchechterView Answer on Stackoverflow
Solution 4 - UinavigationbarJakub TruhlářView Answer on Stackoverflow
Solution 5 - UinavigationbaryuanjileeView Answer on Stackoverflow
Solution 6 - UinavigationbarcookednickView Answer on Stackoverflow
Solution 7 - UinavigationbarRaunakView Answer on Stackoverflow
Solution 8 - UinavigationbarkalpaView Answer on Stackoverflow