SwiftUI导航栏加内联搜索栏时返回按钮标题消失的解决办法
导航栏返回按钮标题消失问题及解决方案探讨
背景
我们有一个基于UIKit搭建的项目,需要在SwiftUI新视图的导航栏中添加与backBarButtonItem内联的搜索栏,searchable修饰符无法满足该需求。
问题
进行概念验证时发现,跳转到新视图后,backBarButtonItem的标题会先显示随即消失,用户体验较差。
初始疑问
- 是否有解决办法?
- 为何会出现该行为?
更新内容
感谢Sweeper的提示,当把barButtonTitle改为短字符串(如"A")时,标题可正常保留。因此新问题变为:
- 如何提高
backBarButtonItem的压缩阻力? - 如何阻止返回按钮标题被隐藏?
我搜索相关问题未找到有效方案,若SwiftUI和UIKit均难以实现,会考虑说服设计师不显示backButtonTitle。
代码示例
UIKit 中的 ViewController
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white title = "Demo" navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(didSelectCancel(sender:))) } @objc func didSelectCancel(sender: UIBarButtonItem) { let hostingController = UIHostingController(rootView: MyView()) self.navigationController?.pushViewController(hostingController, animated: true) } }
SwiftUI 中的 MyView
import SwiftUI struct MyView: View { var body: some View { ScrollView { Text("Hello") } .toolbar { ToolbarItem(placement: .principal) { SearchBar(text: .constant("test"), placeholder: "test") } } } }
SearchBar 实现
由于SwiftUI未提供独立SearchBar,我采用了用于概念验证的第三方实现:
struct SearchBar: UIViewRepresentable { @Binding var text: String var placeholder: String class Coordinator: NSObject, UISearchBarDelegate { @Binding var text: String init(text: Binding<String>) { _text = text } func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { text = searchText } } func makeCoordinator() -> SearchBar.Coordinator { return Coordinator(text: $text) } func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar { let searchBar = UISearchBar(frame: .zero) searchBar.delegate = context.coordinator searchBar.placeholder = placeholder searchBar.searchBarStyle = .minimal searchBar.autocapitalizationType = .none return searchBar } func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) { uiView.text = text } }
解决方案与原因分析
行为原因
导航栏布局优先级中,principal位置的视图(此处为搜索栏)会优先占据可用空间。当搜索栏加载完成后,会挤压返回按钮的标题区域——若标题长度超出剩余空间,系统会自动隐藏标题以适配布局。短标题能保留,是因为其所需空间小于剩余可用宽度。
解决办法
自定义返回按钮并提高压缩阻力
在push新的UIHostingController时,直接配置返回按钮的压缩优先级,确保它不会被搜索栏挤压:@objc func didSelectCancel(sender: UIBarButtonItem) { let hostingController = UIHostingController(rootView: MyView()) // 自定义返回按钮,设置最高水平压缩阻力 let backButton = UIBarButtonItem(title: "Demo", style: .plain, target: nil, action: nil) backButton.setContentCompressionResistancePriority(.required, for: .horizontal) hostingController.navigationItem.leftBarButtonItem = backButton self.navigationController?.pushViewController(hostingController, animated: true) }注意:自定义返回按钮会覆盖系统默认箭头,需手动添加箭头图标并处理返回逻辑,或通过自定义
UIView封装带箭头的返回按钮样式。SwiftUI中完全自定义返回按钮
隐藏系统默认返回按钮,自行实现包含标题和箭头的返回按钮,完全控制其布局不受挤压:struct MyView: View { @Environment(\.dismiss) private var dismiss var body: some View { ScrollView { Text("Hello") } .toolbar { ToolbarItem(placement: .navigationBarLeading) { HStack(spacing: 4) { Image(systemName: "chevron.left") Text("Demo") } .onTapGesture { dismiss() } } ToolbarItem(placement: .principal) { SearchBar(text: .constant("test"), placeholder: "test") } } .navigationBarBackButtonHidden(true) } }全局调整导航栏按钮布局优先级
通过UIAppearance全局设置所有UIBarButtonItem的压缩阻力,这种方式会影响全局导航栏行为,需谨慎使用:// 在App启动时(如AppDelegate/SceneDelegate)添加 UIBarButtonItem.appearance().setContentCompressionResistancePriority(.required, for: .horizontal)
备选方案
若上述方法均无法满足需求,最简单的方式是隐藏返回按钮标题,仅保留箭头:
// 在ViewController的viewDidLoad中添加 navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
内容的提问来源于stack exchange,提问作者vc7
相关产品推荐
相关产品推荐

