Swift中能否通过Extension为UIModalTransitionStyle枚举新增push case?
嘿,这个问题我之前也纠结过,先给你划重点:你绝对没办法通过Swift Extension给UIModalTransitionStyle这类系统枚举新增case!
Swift的Extension确实能给已有类型(类、结构体、枚举、协议)添加功能,但不包括给枚举新增成员值(case)。枚举的case是类型定义的核心部分,必须在原始枚举声明时就确定,后续的Extension只能扩展方法、计算属性、协议实现这类功能,没法修改枚举的成员集合。
而UIModalTransitionStyle是Apple定义的系统枚举,它的四个case(coverVertical、flipHorizontal、crossDissolve、partialCurl)是编译时就固定死的,我们完全没有权限给它加新的push case。
既然没法扩展枚举case,那要实现push风格的模态转场,就得用iOS的自定义转场API——通过UIViewControllerTransitioningDelegate和UIViewControllerAnimatedTransitioning来实现自己的动画逻辑。下面是具体步骤:
1. 创建自定义转场动画器
这个类负责实现push动画的具体逻辑:
class PushTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning { // 动画时长 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.35 } // 核心动画实现 func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let toVC = transitionContext.viewController(forKey: .to), let fromVC = transitionContext.viewController(forKey: .from) else { transitionContext.completeTransition(false) return } let containerView = transitionContext.containerView // 目标VC初始在屏幕右侧 toVC.view.frame = CGRect(x: containerView.bounds.width, y: 0, width: containerView.bounds.width, height: containerView.bounds.height) containerView.addSubview(toVC.view) // 执行滑入动画 UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { fromVC.view.frame = CGRect(x: -containerView.bounds.width, y: 0, width: containerView.bounds.width, height: containerView.bounds.height) toVC.view.frame = containerView.bounds }) { _ in // 恢复原VC的位置,避免后续交互出问题 fromVC.view.frame = containerView.bounds transitionContext.completeTransition(!transitionContext.transitionWasCancelled) } } }
2. 设置转场代理
在你要present的目标ViewController中,设置transitioningDelegate来使用自定义动画:
class PushPresentedVC: UIViewController { private let transitionAnimator = PushTransitionAnimator() override func viewDidLoad() { super.viewDidLoad() transitioningDelegate = self // 注意设置合适的模态展示样式,比如.fullScreen modalPresentationStyle = .fullScreen } } // 实现转场代理方法 extension PushPresentedVC: UIViewControllerTransitioningDelegate { func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return transitionAnimator } // 如果需要dismiss时的反向动画,也可以实现这个方法 func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { // 这里可以写一个反向的滑出动画,比如让VC从左侧滑出 return nil // 替换成反向动画器即可 } }
3. 发起模态跳转
最后,在发起present的地方正常调用present方法,就会自动使用你定义的push风格动画了:
let pushVC = PushPresentedVC() present(pushVC, animated: true)
如果你需要在多个地方复用这个push转场,可以把转场代理的逻辑封装成一个单独的类,或者给UIViewController写个Extension来简化调用,这样就不用每个VC都重复写代理代码啦。
内容的提问来源于stack exchange,提问作者Krunal

