You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI导航栏加内联搜索栏时返回按钮标题消失的解决办法

导航栏返回按钮标题消失问题及解决方案探讨

背景

我们有一个基于UIKit搭建的项目,需要在SwiftUI新视图的导航栏中添加与backBarButtonItem内联的搜索栏,searchable修饰符无法满足该需求。

问题

进行概念验证时发现,跳转到新视图后,backBarButtonItem的标题会先显示随即消失,用户体验较差。

初始疑问

  1. 是否有解决办法?
  2. 为何会出现该行为?

更新内容

感谢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位置的视图(此处为搜索栏)会优先占据可用空间。当搜索栏加载完成后,会挤压返回按钮的标题区域——若标题长度超出剩余空间,系统会自动隐藏标题以适配布局。短标题能保留,是因为其所需空间小于剩余可用宽度。

解决办法

  1. 自定义返回按钮并提高压缩阻力
    在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封装带箭头的返回按钮样式。

  2. 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)
        }
    }
    
  3. 全局调整导航栏按钮布局优先级
    通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 02:34:59