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

SwiftUI如何自定义支持View类型的导航标题并保留原生动画?

实现SwiftUI自定义View类型导航标题(保留原生特性)

核心方案:通过UIKit桥接复用原生导航栏特性

SwiftUI原生.navigationTitle仅支持字符串,但可以借助UIViewControllerRepresentable与UIKit互操作,利用UINavigationItem的titleView属性设置自定义视图,完全保留原生导航栏的滚动锚定、内联变形等动画,无需自行实现。

具体实现代码

1. 构建UIKit桥接容器

创建承载SwiftUI自定义视图的UIViewControllerRepresentable组件:

import SwiftUI

struct CustomNavigationTitleHost<Content: View>: UIViewControllerRepresentable {
    let content: Content
    
    func makeUIViewController(context: Context) -> UIHostingController<Content> {
        let hostingController = UIHostingController(rootView: content)
        hostingController.view.backgroundColor = .clear
        hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        return hostingController
    }
    
    func updateUIViewController(_ uiViewController: UIHostingController<Content>, context: Context) {
        uiViewController.rootView = content
    }
}

2. 扩展View添加自定义修饰符

给View扩展一个类似原生语法的修饰符,方便调用:

extension View {
    func customNavigationTitle<Content: View>(@ViewBuilder content: () -> Content) -> some View {
        self.background(
            CustomNavigationTitleHost(content: content())
                .frame(height: 44) // 匹配原生导航栏标题高度
                .accessibilityHidden(true)
                .onAppear {
                    // 获取当前导航控制器并设置titleView
                    guard let navController = UIApplication.shared.windows.first?.rootViewController as? UINavigationController else { return }
                    navController.topViewController?.navigationItem.titleView = navController.topViewController?.view.subviews.first {
                        $0 is UIHostingController<Content>.RootView
                    }?.superview
                }
        )
    }
}

3. 使用示例

直接在NavigationStack/NavigationView内调用,传入任意SwiftUI视图作为标题:

struct DemoView: View {
    var body: some View {
        NavigationStack {
            ScrollView {
                ForEach(0..<50) { index in
                    Text("列表项 \(index)")
                        .padding()
                }
            }
            .customNavigationTitle {
                HStack(spacing: 8) {
                    Image(systemName: "heart.fill")
                        .foregroundColor(.red)
                    Text("Fitness")
                        .font(.title)
                        .fontWeight(.bold)
                }
            }
            .navigationBarTitleDisplayMode(.inline)
        }
    }
}

关键注意事项

  • 必须在NavigationStack(iOS 16+)或NavigationView(iOS 15及以下)内部使用该修饰符
  • 自定义视图高度建议设为44pt,匹配原生导航栏标题默认高度,避免布局错乱
  • 若需横竖屏适配,可在CustomNavigationTitleHost的updateUIViewController方法中调整视图尺寸

开源库替代方案

不想自行实现桥接逻辑的话,可使用成熟开源库:

  • SwiftUIX:提供增强版导航组件,支持自定义View类型标题,完全兼容原生动画
  • NavigationStackEx:针对NavigationStack扩展,使用方式与原生修饰符高度一致

内容的提问来源于stack exchange,提问作者Sheldon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 02:21:02