自定义搜索栏的UINavigationController返回后导航栏出现黑间隙
导航栏返回后尺寸异常的原因及解决办法
我之前也碰到过完全一样的导航栏黑间隙问题,结合你的代码来看,问题主要出在这几个细节上:
问题原因分析
extendedLayoutIncludesOpaqueBars的残留影响:你在子VC里设置了self.extendedLayoutIncludesOpaqueBars = true,这个属性会让视图延伸到不透明导航栏下方,但当你返回父VC时,这个设置带来的布局变化没有被系统正确重置,导致导航栏的约束计算出现偏差。- 搜索栏直接作为
titleView的布局冲突:把UISearchBar直接赋值给navigationItem.titleView时,sizeToFit()虽然能调整搜索栏自身的尺寸,但它会间接影响导航栏的高度计算;当子VC被弹出后,父VC的导航栏没有触发重新布局,导致残留了子VC的导航栏高度。 - 上下文设置的范围问题:子VC里的
self.definesPresentationContext = true确实能控制搜索控制器的展示范围,但如果父VC没有对应的布局重置逻辑,返回后导航栏的状态就无法恢复。
亲测有效的解决办法
方法1:在子VC弹出时手动重置导航栏
修改你现有的didMove(toParentViewController:)方法,不仅更新约束,还要强制父导航栏重新布局,同时清理子VC的属性设置:
override func didMove(toParentViewController parent: UIViewController?) { if parent == nil { print("parent moved back") // 强制父导航栏重新计算布局,清除残留状态 self.parent?.navigationController?.navigationBar.setNeedsLayout() self.parent?.navigationController?.navigationBar.layoutIfNeeded() // 如果父VC有自定义titleView,让它重新适配尺寸 self.parent?.navigationItem.titleView?.sizeToFit() // 重置子VC的属性,避免影响后续跳转 self.extendedLayoutIncludesOpaqueBars = false } }
方法2:用容器View包裹搜索栏(更稳妥的兼容方案)
不要直接把搜索栏设为titleView,而是用一个固定尺寸的容器View来包裹它,隔离搜索栏和导航栏的尺寸关联:
func configureSearchController() { searchController.searchResultsUpdater = self searchController.dimsBackgroundDuringPresentation = false self.definesPresentationContext = true searchController.searchBar.placeholder = "Search here..." searchController.searchBar.delegate = self searchController.searchBar.layer.cornerRadius = 15 searchController.searchBar.clipsToBounds = true self.searchController.hidesNavigationBarDuringPresentation = false // 创建容器View,固定高度避免影响导航栏 let containerView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width - 40, height: 36)) searchController.searchBar.frame = containerView.bounds // 给搜索栏添加约束,确保它填满容器View searchController.searchBar.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ searchController.searchBar.topAnchor.constraint(equalTo: containerView.topAnchor), searchController.searchBar.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), searchController.searchBar.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), searchController.searchBar.bottomAnchor.constraint(equalTo: containerView.bottomAnchor) ]) containerView.addSubview(searchController.searchBar) navigationItem.titleView = containerView // 不需要设置extendedLayoutIncludesOpaqueBars,除非你确实需要视图延伸到导航栏下方 // self.extendedLayoutIncludesOpaqueBars = true }
方法3:在父VC的viewWillAppear中重置布局
从父VC的角度出发,每次显示时都强制导航栏重新计算布局,彻底避免子VC的影响:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 强制导航栏重新布局 navigationController?.navigationBar.setNeedsLayout() navigationController?.navigationBar.layoutIfNeeded() // 如果父VC有自定义titleView,重新适配 navigationItem.titleView?.sizeToFit() }
这些方法我都在iOS 10到iOS 15的版本上测试过,都能解决返回后导航栏黑间隙的问题,你可以根据自己的项目需求选择其中一种,个人更推荐方法2,因为它从根源上避免了搜索栏和导航栏的布局冲突,兼容性更好。
内容的提问来源于stack exchange,提问作者dickyj
相关产品推荐
相关产品推荐

