I'm transitioning from a view with a large style UINavigationItem
into a view that has a large style UINavigationItem
and a UISearchController
. I've customized the background colour of the UINavigationBar
.
For some reason, there's a 1px line or space between the UINavigationBar
and the UISearchController
. I can tell it's from the NavigationBar
, because if I scroll so that the search bar sticks to the top, the line disappears. I'm just not sure what's generating this line. I'm not using shadows, or anything fancy, other than setting the barTintColor
and the tintColor
. If I don't use large style titles, I see the line, but only when transitioning between views. It's like when transitioning the UISearchController
just doesn't stick right to the UINavigationBar
.
Any help in figuring out where this line is coming from is appreciated.
Update: On some further investigation, it seems this only happens when navigationItem.hidesSearchBarWhenScrolling
is set to false
. If it initially is hidden, then the animation from the previous view is smooth, and showing the bar is also smooth.
Testing the navigation item and search bar setup you've described, I could only reproduce the 1px/point line during the transition animations.
Workaround: This line is actually a background layout view (forLastBaselineLayout
) used by UINavigationBar
ending up visible due to the buggy framing of UISearchBar
. Set its background to match your navigation bar color to hide it in hind site
// MARK: UINavigationControllerDelegate
public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
viewController.navigationController?.navigationBar
.forLastBaselineLayout.backgroundColor = .red // TODO:
}
Make sure to set the object implementing UINavigationControllerDelegate
is set as navigationController.delegate
to receive the above delegate call.
In addition (not part of the question): UISearchBar
seems to behave strangely when transitioning (push and pop) between controllers especially when one has/show a searchBar
but not the other. Workaround, I found it visually more pleasing to temporarily hide the search bar from presenting controllers. Here is the code I use:
public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if let sender = navigationStack.popLast() {
prepareSearchBarForTransition(from: sender)
}
navigationStack = navigationController.children
}
/// Set only from `navigationController: willShow`
private var navigationStack: [UIViewController] = []
func prepareSearchBarForTransition(from sender: UIViewController) {
if #available(iOS 11.0, *) {
let searchController = sender.navigationItem.searchController
sender.navigationItem.searchController = nil
weak var weakSender = sender
let navigationTransitionDuration: TimeInterval = 0.33
DispatchQueue.main.asyncAfter(deadline: .now() + navigationTransitionDuration) {
/// Reset the search bar.
weakSender?.navigationItem.searchController = searchController
}
} else {
// TODO: Check if the above workaround is neccessary for < iOS 11
}
}