SwiftUI自定义视图转场实现:从右到左全屏呈现,支持双视图动画
嘿,我之前正好做过一模一样的需求!下面给你分享一套方案,能实现和UIKit里UIViewControllerAnimatedTransitioning一样灵活的自定义转场——支持从右到左的全屏呈现,还能分别控制父视图和被呈现视图的动画效果,完全满足你的需求。
核心思路
SwiftUI的原生转场(比如fullScreenCover)虽然便捷,但灵活性远不如UIKit的转场系统。我们可以通过桥接UIKit的转场框架,复用UIViewControllerAnimatedTransitioning和UIViewControllerTransitioningDelegate来实现完全自定义的动画逻辑,同时保持SwiftUI的声明式语法体验。
实现步骤
1. 自定义动画过渡控制器
首先创建一个遵循UIViewControllerAnimatedTransitioning的类,这是控制转场动画的核心,你可以在这里定义呈现/消失时的所有动画细节:
class CustomTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning { let isPresenting: Bool init(isPresenting: Bool) { self.isPresenting = isPresenting super.init() } // 自定义过渡时长 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.5 } // 核心动画逻辑 func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let containerView = transitionContext.containerView guard let toVC = transitionContext.viewController(forKey: .to), let fromVC = transitionContext.viewController(forKey: .from) else { transitionContext.completeTransition(false) return } if isPresenting { // 呈现动画:被呈现视图从右侧滑入,父视图缩小+变暗 toVC.view.frame = containerView.frame.offsetBy(dx: containerView.bounds.width, dy: 0) containerView.addSubview(toVC.view) UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: .curveEaseInOut) { toVC.view.frame = containerView.frame fromVC.view.transform = CGAffineTransform(scaleX: 0.95, y: 0.95) fromVC.view.alpha = 0.7 } completion: { _ in transitionContext.completeTransition(true) } } else { // 消失动画:被呈现视图滑出右侧,父视图恢复原状 UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: .curveEaseInOut) { fromVC.view.frame = containerView.frame.offsetBy(dx: containerView.bounds.width, dy: 0) toVC.view.transform = .identity toVC.view.alpha = 1.0 } completion: { _ in transitionContext.completeTransition(true) } } } }
2. 创建转场代理类
接下来实现UIViewControllerTransitioningDelegate,用来告诉系统使用我们自定义的动画控制器:
class CustomTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate { func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return CustomTransitionAnimator(isPresenting: true) } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return CustomTransitionAnimator(isPresenting: false) } }
3. 包装SwiftUI视图的容器
用UIViewControllerRepresentable把SwiftUI视图包装成UIKit的UIHostingController,并绑定呈现状态:
struct TransitionContainerView<Content: View>: UIViewControllerRepresentable { let content: Content @Binding var isPresented: Bool private let transitionDelegate = CustomTransitionDelegate() func makeUIViewController(context: Context) -> UIHostingController<Content> { let hostingVC = UIHostingController(rootView: content) hostingVC.modalPresentationStyle = .custom hostingVC.transitioningDelegate = transitionDelegate return hostingVC } func updateUIViewController(_ uiViewController: UIHostingController<Content>, context: Context) { // 更新视图内容 uiViewController.rootView = content // 处理呈现/消失状态 if isPresented, !uiViewController.isBeingPresented { uiViewController.presentingViewController?.present(uiViewController, animated: true) } else if !isPresented, uiViewController.presentingViewController != nil { uiViewController.dismiss(animated: true) } } }
4. 封装成SwiftUI风格的修饰符
为了让使用更便捷,我们给View扩展一个自定义修饰符,用法和原生的fullScreenCover一致:
extension View { func customFullScreenTransition<Content: View>(isPresented: Binding<Bool>, @ViewBuilder content: @escaping () -> Content) -> some View { self.background( TransitionContainerView(content: content(), isPresented: isPresented) ) } }
示例用法
现在你可以像使用原生转场一样,轻松实现自定义的从右到左全屏转场:
struct ContentView: View { @State private var isPresentingDetail = false var body: some View { Button("打开详情页") { isPresentingDetail = true } .customFullScreenTransition(isPresented: $isPresentingDetail) { Color.blue .overlay( Button("关闭") { isPresentingDetail = false } .foregroundColor(.white) .font(.title) ) } } }
进阶优化建议
- 可配置动画参数:可以把动画时长、缩放比例、透明度等做成参数,让转场更灵活适配不同场景。
- 交互式转场:如果需要滑动返回等交互效果,可以实现
UIViewControllerInteractiveTransitioning协议,添加手势识别器。 - 自定义转场方向:修改
animateTransition里的offsetBy参数,就能实现从左、上、下等不同方向的入场动画。
内容的提问来源于stack exchange,提问作者Joseph Francis
相关产品推荐
相关产品推荐

