iOS 11下UINavigationBar中UISearchBar与UIBarButtonItem重叠问题求助
这个问题是iOS 11导航栏布局系统变更后出现的已知问题——当你动态修改navigationItem的左右BarButtonItem时,导航栏没有正确更新作为titleView的UISearchBar的布局约束,导致两者重叠。下面给你几个可靠的解决思路:
方法一:修改BarButtonItem后强制更新布局
在你设置leftBarButtonItem的代码之后,手动触发导航栏的布局更新,让它重新计算所有元素的位置:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) if traitCollection.horizontalSizeClass == .regular { navigationItem.leftBarButtonItem = button } else { navigationItem.leftBarButtonItem = nil } // 强制导航栏重新布局 navigationController?.navigationBar.setNeedsLayout() navigationController?.navigationBar.layoutIfNeeded() }
这个方法最简单,通过手动触发布局,让导航栏重新计算titleView和BarButtonItem的位置,直接修复重叠问题。
方法二:给UISearchBar套一个容器View作为titleView
iOS 11对直接使用UISearchBar作为titleView的布局处理存在bug,把它放在一个容器View里可以规避这个问题,容器View会正确响应导航栏的布局变化:
override func viewDidLoad() { super.viewDidLoad() let searchBar = UISearchBar() searchBar.searchBarStyle = .minimal // 创建容器View let containerView = UIView() containerView.addSubview(searchBar) // 给searchBar添加约束,让它充满容器 searchBar.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ searchBar.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), searchBar.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), searchBar.topAnchor.constraint(equalTo: containerView.topAnchor), searchBar.bottomAnchor.constraint(equalTo: containerView.bottomAnchor) ]) navigationItem.titleView = containerView }
容器View会作为导航栏的titleView,它的intrinsicContentSize由内部的UISearchBar决定,当BarButtonItem变化时,导航栏能正确调整容器View的宽度,从而避免重叠。
方法三:自定义UISearchBar的intrinsicContentSize
如果上面的方法都不生效,你可以尝试重写UISearchBar的intrinsicContentSize,确保它在布局时能正确计算自身宽度:
class CustomSearchBar: UISearchBar { override var intrinsicContentSize: CGSize { var size = super.intrinsicContentSize // 根据导航栏可用宽度调整自身宽度 if let navBar = self.superview as? UINavigationBar, let navItem = self.navigationItem { let availableWidth = navBar.bounds.width - (navBar.layoutMargins.left + navBar.layoutMargins.right) // 减去左右BarButtonItem的预估宽度,预留间距 let buttonTotalWidth = (navItem.leftBarButtonItem?.width ?? 0) + (navItem.rightBarButtonItem?.width ?? 0) size.width = availableWidth - buttonTotalWidth - 20 } return size } }
然后在代码里使用CustomSearchBar代替原生的UISearchBar,并且在修改BarButtonItem后调用navigationItem.titleView?.invalidateIntrinsicContentSize()来触发重新计算。
补充说明
你的操作本身没有错误,这个是iOS 11导航栏布局系统的兼容性bug——之前iOS 10及更早版本使用手动布局逻辑,iOS 11切换到自动布局后,动态修改BarButtonItem时的布局更新逻辑存在疏漏,导致titleView没有被正确调整。
内容的提问来源于stack exchange,提问作者Automatic

