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

SwiftUI中如何实现NavigationView转场联动自定义进度视图动画?

在SwiftUI中实现与导航转场联动的进度视图(支持侧滑实时更新)

完全可行。虽然SwiftUI原生的NavigationStack/NavigationView没有直接暴露转场进度的API,但可以通过桥接UIKit的UINavigationController实现,思路和你在UIKit中的做法一致,只需用SwiftUI的视图包装器和ObservableObject传递状态。

实现步骤

1. 定义进度管理的ObservableObject

这个类负责存储导航栈深度、转场进度,并提供状态更新逻辑,让SwiftUI视图能响应变化:

import SwiftUI
import UIKit

class NavigationProgressManager: ObservableObject {
    // 当前导航栈深度(根视图为0,每push一个页面加1)
    @Published var stackDepth: Int = 0
    // 转场实时进度(0~1,对应转场完成百分比)
    @Published var transitionProgress: CGFloat = 0.0
    
    private weak var navigationController: UINavigationController?
    
    func setup(with navController: UINavigationController) {
        self.navigationController = navController
        updateStackDepth()
        
        // 监听侧滑返回手势
        navController.interactivePopGestureRecognizer?.addTarget(self, action: #selector(handlePopGesture))
        
        // 监听导航转场完成事件
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleTransitionComplete),
            name: NSNotification.Name("UINavigationControllerDidShowViewController"),
            object: navController
        )
    }
    
    @objc private func handlePopGesture(_ gesture: UIGestureRecognizer) {
        guard let coordinator = navigationController?.transitionCoordinator else { return }
        
        switch gesture.state {
        case .began, .changed:
            transitionProgress = coordinator.percentageComplete
        case .ended, .cancelled:
            transitionProgress = 0.0
            updateStackDepth()
        default: break
        }
    }
    
    @objc private func handleTransitionComplete(_ notification: Notification) {
        updateStackDepth()
        transitionProgress = 0.0
    }
    
    private func updateStackDepth() {
        stackDepth = navigationController?.viewControllers.count ?? 0
    }
}

2. 创建自定义导航控制器的SwiftUI包装器

通过UIViewControllerRepresentable将UIKit的UINavigationController接入SwiftUI,并关联进度管理器:

struct CustomNavigationView<Content: View>: UIViewControllerRepresentable {
    @ViewBuilder let content: () -> Content
    @ObservedObject var progressManager: NavigationProgressManager
    
    func makeUIViewController(context: Context) -> UINavigationController {
        let navController = UINavigationController()
        let rootVC = UIHostingController(rootView: content())
        navController.setViewControllers([rootVC], animated: false)
        
        progressManager.setup(with: navController)
        return navController
    }
    
    func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {
        if let rootVC = uiViewController.viewControllers.first as? UIHostingController<Content> {
            rootVC.rootView = content()
        }
    }
}

3. 在SwiftUI中绑定进度视图

将进度管理器的状态绑定到自定义ProgressView,实现转场联动:

struct ContentView: View {
    @StateObject private var progressManager = NavigationProgressManager()
    
    var body: some View {
        VStack(spacing: 16) {
            // 自定义进度视图,根据栈深度和转场进度计算当前进度
            CustomProgressView(
                totalPages: 3,
                currentProgress: calculateProgress()
            )
            
            CustomNavigationView(progressManager: progressManager) {
                RootPage()
            }
        }
    }
    
    private func calculateProgress() -> CGFloat {
        // 假设总共有3个页面,栈深度从1(根视图)到3(最后一页)
        let baseProgress = CGFloat(progressManager.stackDepth - 1) / 2
        // 侧滑返回时,进度随手势反向变化
        let adjustedProgress = baseProgress - progressManager.transitionProgress / 2
        return max(0, min(1, adjustedProgress))
    }
}

// 示例自定义进度视图
struct CustomProgressView: View {
    let totalPages: Int
    let currentProgress: CGFloat
    
    var body: some View {
        GeometryReader { geo in
            ZStack(alignment: .leading) {
                Rectangle()
                    .fill(Color.gray.opacity(0.3))
                    .frame(height: 8)
                
                Rectangle()
                    .fill(Color.blue)
                    .frame(width: geo.size.width * currentProgress, height: 8)
                    .cornerRadius(4)
            }
            .cornerRadius(4)
        }
        .frame(height: 8)
        .padding(.horizontal)
    }
}

// 示例页面
struct RootPage: View {
    var body: some View {
        NavigationStack {
            VStack {
                Text("根页面")
                NavigationLink("进入页面1") {
                    Page1()
                }
            }
            .navigationTitle("首页")
        }
    }
}

struct Page1: View {
    var body: some View {
        VStack {
            Text("页面1")
            NavigationLink("进入页面2") {
                Page2()
            }
        }
        .navigationTitle("页面1")
    }
}

struct Page2: View {
    var body: some View {
        Text("页面2")
            .navigationTitle("页面2")
    }
}

关键说明

  • 代码通过监听UINavigationController的interactivePopGestureRecognizer和transitionCoordinator,实时获取转场进度并同步到SwiftUI视图。
  • 如需支持push转场的进度监听,可以在UINavigationController的pushViewController方法中扩展逻辑,通过transitionCoordinator获取push的实时进度。
  • calculateProgress方法需根据你的实际页面数量调整,确保进度计算符合业务预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 12:15:57