如何不使用Storyboard实现ViewController间的转场动画?
完全可以脱离Storyboard实现自定义转场动画!
当然没问题——Storyboard只是iOS开发中可视化构建界面的工具,自定义ViewController转场的核心是依靠UIViewControllerTransitioningDelegate和UIViewControllerAnimatedTransitioning协议,纯代码完全能实现你想要的效果。你已经搭建了正确的代码框架,我来帮你梳理细节和注意事项:
你的现有代码解析
你给出的代码已经走在正确的方向上:
触发转场的方法:
@objc func handleAccount() { let navController = UINavigationController(rootViewController: userButtonLauncher()) navController.transitioningDelegate = self navController.modalPresentationStyle = .custom self.present(navController, animated: true, completion: nil) }这里通过代码实例化
UINavigationController作为容器,设置transitioningDelegate为当前控制器,同时将modalPresentationStyle设为.custom——这是启用自定义转场的必要配置,非常正确。转场代理方法:
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { transition.transitionMode = .present transition.startingPoint = CGPoint(x: 0, y: 0) return transition } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { transition.transitionMode = .pop transition.startingPoint = CGPoint(x: 0, y: 0) return transition }你正确实现了
UIViewControllerTransitioningDelegate的两个核心方法,分别为present和dismiss转场返回自定义的动画控制器,并且通过transitionMode区分了两种转场状态,这是自定义转场的关键逻辑。
关键注意事项
要让转场正常工作,还需要确保以下几点:
- 自定义动画控制器的实现:你代码中的
transition对象必须是遵循UIViewControllerAnimatedTransitioning协议的自定义类,必须实现两个强制方法:transitionDuration(using:):返回转场动画的时长animateTransition(using:):编写具体的动画逻辑(比如缩放、平移、淡入淡出等)
- 状态重置:如果复用同一个
transition实例,要确保每次转场前正确设置transitionMode和startingPoint(你已经做到了这一点) - 内存管理:如果
transition是当前控制器的属性,避免循环引用(比如不需要持有引用的话,可以每次创建新实例,或者用弱引用管理)
补充完整的自定义动画控制器示例
如果还没实现transition对应的类,这里给一个简单的缩放转场示例:
class CustomTransition: NSObject, UIViewControllerAnimatedTransitioning { // 定义转场模式 enum TransitionMode { case present case dismiss } var transitionMode: TransitionMode = .present var startingPoint: CGPoint = .zero // 动画时长 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.3 } // 核心动画逻辑 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 } switch transitionMode { case .present: // Present动画:从起始点缩放展开 toVC.view.frame = CGRect(origin: startingPoint, size: CGSize(width: 10, height: 10)) toVC.view.clipsToBounds = true toVC.view.layer.cornerRadius = 5 containerView.addSubview(toVC.view) UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.5, options: .curveEaseOut) { toVC.view.frame = containerView.bounds toVC.view.layer.cornerRadius = 0 } _ in transitionContext.completeTransition(true) } case .dismiss: // Dismiss动画:缩放到起始点后消失 UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { fromVC.view.frame = CGRect(origin: self.startingPoint, size: CGSize(width: 10, height: 10)) fromVC.view.layer.cornerRadius = 5 }) { _ in fromVC.view.removeFromSuperview() transitionContext.completeTransition(true) } } } }
最终整合
在你的主ViewController中,只需声明遵循UIViewControllerTransitioningDelegate协议,并持有CustomTransition实例即可:
class MainViewController: UIViewController, UIViewControllerTransitioningDelegate { private let transition = CustomTransition() // ... 其他视图初始化、布局代码 @objc func handleAccount() { let targetVC = userButtonLauncher() // 你的目标ViewController let navController = UINavigationController(rootViewController: targetVC) navController.transitioningDelegate = self navController.modalPresentationStyle = .custom present(navController, animated: true, completion: nil) } // 实现转场代理方法 func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { transition.transitionMode = .present transition.startingPoint = CGPoint(x: 0, y: 0) // 可以根据需要调整起始点,比如按钮的位置 return transition } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { transition.transitionMode = .dismiss transition.startingPoint = CGPoint(x: 0, y: 0) return transition } }
这样就能完全脱离Storyboard实现自定义转场动画了,整个流程都是纯代码控制,灵活性更高。
内容的提问来源于stack exchange,提问作者eksglox
相关产品推荐
相关产品推荐

