UIModalTransitionStyle.coverVertical转场禁用蒙层变暗动画
解决方案
核心逻辑:弃用系统转场自带的蒙层渐变逻辑,将蒙层显示和模态窗上滑两个效果拆解开控制,蒙层直接瞬间添加不做动画,仅保留模态窗的上滑过渡。
快速实现(代码量少,适合快速修改)
直接在原有代码基础上调整,手动提前添加蒙层,自定义模态窗上滑动画,不需要引入额外的转场类:
if let subjectsListViewController = self.storyboard?.instantiateViewController(withIdentifier: "SubjectsListViewController") as? SubjectsListViewController { subjectsListViewController.delegate = self subjectsListViewController.modalPresentationStyle = .overCurrentContext subjectsListViewController.modalTransitionStyle = .crossDissolve // 1. 瞬间添加变暗蒙层,无任何过渡动画 let dimmingView = UIView(frame: self.view.bounds) dimmingView.backgroundColor = .black.withAlphaComponent(0.4) dimmingView.tag = 999 // 用于dismiss时精准移除 self.view.addSubview(dimmingView) // 2. 设置模态窗动画起始位置:屏幕底部外 let screenHeight = UIScreen.main.bounds.height subjectsListViewController.view.transform = CGAffineTransform(translationX: 0, y: screenHeight) present(subjectsListViewController, animated: true, completion: nil) // 3. 仅执行模态窗上滑动画,时长与系统默认coverVertical一致 UIView.animate(withDuration: 0.35, delay: 0, options: .curveEaseOut) { subjectsListViewController.view.transform = .identity } }
注意:dismiss模态窗时,需要找到tag为999的蒙层手动移除,同时可以给模态窗加对应的下滑动画,和系统原生交互逻辑保持一致。
标准实现(推荐,稳定性高)
使用苹果官方推荐的UIPresentationController自定义转场逻辑,完全拆分蒙层和模态窗的动画,不需要手动管理视图生命周期,不会出现时序错乱问题:
- 首先自定义PresentationController,单独管理蒙层,蒙层初始化时直接设置为最终可见状态,不添加渐变动画:
class InstantDimmingPresentationController: UIPresentationController { private lazy var dimmingView: UIView = { let view = UIView() view.backgroundColor = .black.withAlphaComponent(0.4) view.alpha = 1 // 直接设为最终透明度,不做渐变 return view }() override func presentationTransitionWillBegin() { super.presentationTransitionWillBegin() guard let containerView = containerView else { return } dimmingView.frame = containerView.bounds containerView.insertSubview(dimmingView, at: 0) } override func dismissalTransitionWillBegin() { super.dismissalTransitionWillBegin() dimmingView.removeFromSuperview() } override var frameOfPresentedViewInContainerView: CGRect { containerView?.bounds ?? .zero } }
- 实现仅负责模态窗上滑的动画控制器,不处理任何蒙层逻辑:
class CoverVerticalOnlyAnimator: NSObject, UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 0.35 // 和系统默认coverVertical动画时长对齐 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let toView = transitionContext.view(forKey: .to) else { transitionContext.completeTransition(false) return } let containerView = transitionContext.containerView let screenHeight = UIScreen.main.bounds.height toView.frame = containerView.bounds toView.transform = CGAffineTransform(translationX: 0, y: screenHeight) containerView.addSubview(toView) UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: .curveEaseOut) { toView.transform = .identity } completion: { finished in transitionContext.completeTransition(finished) } } }
- 让弹出的模态控制器遵循
UIViewControllerTransitioningDelegate,返回自定义的转场实例:
extension SubjectsListViewController: UIViewControllerTransitioningDelegate { func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { InstantDimmingPresentationController(presentedViewController: presented, presenting: presenting) } func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { CoverVerticalOnlyAnimator() } }
- 调整原有present代码,使用自定义转场样式:
if let subjectsListViewController = self.storyboard?.instantiateViewController(withIdentifier: "SubjectsListViewController") as? SubjectsListViewController { subjectsListViewController.delegate = self subjectsListViewController.modalPresentationStyle = .custom subjectsListViewController.transitioningDelegate = subjectsListViewController present(subjectsListViewController, animated: true, completion: nil) }
内容的提问来源于stack exchange,提问作者laneboyandrew
相关产品推荐
相关产品推荐

