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

如何获取自定义转场中目标ViewController的UINavigationBar快照?

这个问题我之前踩过坑!核心原因是在animateTransition(using:)方法执行时,目标ViewController(也就是你说的toVC)还没被正式加入导航控制器的视图层级——要么它的navigationController关联还没生效,要么导航栏还没根据toVC的配置完成布局和样式更新,直接快照自然会返回nil。

下面给你两种可靠的解决思路,你可以根据自己的场景选:

方式一:手动构建临时导航栏快照

这种方式不依赖真实导航控制器的导航栏,而是完全复刻toVC的导航栏配置,生成一个临时导航栏再快照,适合需要精准控制样式的场景:

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    guard let toVC = transitionContext.viewController(forKey: .to) else {
        transitionContext.completeTransition(false)
        return
    }
    
    // 1. 创建匹配导航控制器样式的临时导航栏
    let statusBarHeight = UIApplication.shared.statusBarFrame.height
    let navBarFrame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 44 + statusBarHeight)
    let tempNavBar = UINavigationBar(frame: navBarFrame)
    
    // 同步导航栏基础样式
    if let navBar = toVC.navigationController?.navigationBar {
        tempNavBar.barStyle = navBar.barStyle
        tempNavBar.tintColor = navBar.tintColor
        tempNavBar.backgroundColor = navBar.backgroundColor
        tempNavBar.setBackgroundImage(navBar.backgroundImage(for: .default), for: .default)
        tempNavBar.shadowImage = navBar.shadowImage
    }
    
    // 2. 同步toVC的导航栏内容(标题、按钮等)
    let navItem = UINavigationItem(title: toVC.title ?? "")
    navItem.leftBarButtonItems = toVC.navigationItem.leftBarButtonItems
    navItem.rightBarButtonItems = toVC.navigationItem.rightBarButtonItems
    navItem.titleView = toVC.navigationItem.titleView
    tempNavBar.setItems([navItem], animated: false)
    
    // 3. 强制布局,确保内容渲染完成
    tempNavBar.layoutIfNeeded()
    
    // 4. 生成快照
    guard let navBarSnapshot = tempNavBar.snapshotView(afterScreenUpdates: true) else {
        transitionContext.completeTransition(false)
        return
    }
    
    // 后续转场动画逻辑
    let containerView = transitionContext.containerView
    containerView.addSubview(toVC.view)
    toVC.view.isHidden = true // 先隐藏真实视图,用快照做过渡
    containerView.addSubview(navBarSnapshot)
    
    // 这里写你的动画代码...
    
    // 动画结束后清理
    UIView.animate(withDuration: transitionDuration(using: transitionContext)) {
        // 动画效果
    } completion: { _ in
        toVC.view.isHidden = false
        navBarSnapshot.removeFromSuperview()
        transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
    }
}

方式二:临时将toVC加入导航栈触发布局

这种方式更贴近真实场景,适合需要完全复用导航控制器现有样式的情况,但要注意临时修改导航栈的副作用:

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    guard let toVC = transitionContext.viewController(forKey: .to),
          let navController = toVC.navigationController else {
        transitionContext.completeTransition(false)
        return
    }
    
    let containerView = transitionContext.containerView
    containerView.addSubview(toVC.view)
    
    // 1. 临时把toVC推到导航栈,触发导航栏更新
    let originalTopVC = navController.topViewController
    navController.pushViewController(toVC, animated: false)
    
    // 2. 强制导航控制器布局,确保导航栏渲染完成
    navController.view.layoutIfNeeded()
    
    // 3. 获取导航栏快照
    guard let navBarSnapshot = navController.navigationBar.snapshotView(afterScreenUpdates: true) else {
        // 快照失败要恢复原导航栈
        if originalTopVC != nil {
            navController.popViewController(animated: false)
        }
        transitionContext.completeTransition(false)
        return
    }
    
    // 4. 恢复原导航栈(转场完成后系统会正式处理栈变化)
    if originalTopVC != nil {
        navController.popViewController(animated: false)
    }
    
    // 后续动画逻辑
    toVC.view.isHidden = true
    containerView.addSubview(navBarSnapshot)
    
    // 你的动画代码...
    
    UIView.animate(withDuration: transitionDuration(using: transitionContext)) {
        // 动画效果
    } completion: { _ in
        toVC.view.isHidden = false
        navBarSnapshot.removeFromSuperview()
        transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
    }
}

关键注意点

  • 无论哪种方式,都必须调用layoutIfNeeded()强制布局,否则导航栏的内容可能还没渲染,快照依然会为空。
  • 如果你的toVC有特殊配置(比如隐藏导航栏、自定义返回按钮),一定要在代码里同步这些设置,否则快照会和真实效果不一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:16:36