1
votes

In iOS 11, if you set a UISearchBar to be titleView of the navigationItem. Then add / remove UIBarButtonItem to navigationItem.leftBarButtonItems. The UIBarButtonItem will overlap with the UISearchBar.

enter image description here Steps to reproduce:

  1. New single view Xcode project
  2. Embed ViewController in UINavigationController in storyboard
  3. Modify ViewController.swift like this

    class ViewController: UIViewController {
        let button = UIBarButtonItem(barButtonSystemItem: .add, target: nil, action: nil)
        override func viewDidLoad() {
            super.viewDidLoad()
            let searchBar = UISearchBar()
            searchBar.searchBarStyle = .minimal
            navigationItem.titleView = searchBar
        }
    
        override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
            super.traitCollectionDidChange(previousTraitCollection)
            if traitCollection.horizontalSizeClass == .regular {
                navigationItem.leftBarButtonItem = button
            } else {
                navigationItem.leftBarButtonItem = nil
            }
        }
    }
    

Environment: Xcode 9.2, iOS 11.2.2

My question is: is there a way for the UINavigationBar to layout items correctly or have I done something wrong here? Thanks!

3

3 Answers

3
votes

Try DispatchQueue.main.async

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    DispatchQueue.main.async {
        if self.traitCollection.horizontalSizeClass == .regular {
            self.navigationItem.leftBarButtonItem = self.button
        } else {
            self.navigationItem.leftBarButtonItem = nil
        }
    }
}
0
votes
override func viewDidLoad() {
    super.viewDidLoad()

    navigationController?.setNavigationBarHidden(true, animated: false)
    navigationController?.setNavigationBarHidden(false, animated: true)
}
0
votes

Reset the navigationItem.titleView in viewWillAppear.

Set titleView = nil and back to search bar to force force Navigation Bar to layout again.

Something like:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    navigationItem.titleView = nil
    navigationItem.titleView = self.searchBar
}

More details

I like the accepted solution using traitCollectionDidChange.

However, that solution does not work for me on (and only on!) iPhone 11 Pro Max (iOS 14.4 at the moment).

It is a very weird issue, but the reset in viewWillAppear is the only reliable solution I found working on all devices.