UINavigationController推送新视图控制器时动画卡顿异常
问题描述
我希望WelcomeViewController和ViewController2均以UIVisualEffectView作为背景。在推送ViewController2时,动画出现严重卡顿,且推送过程中无法完全向左滑入;但返回(Pop)动画运行正常。请问该如何修复这一异常动画?
最小复现代例
class ViewController: UIViewController { override func loadView() { view = MKMapView() } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } override func viewDidAppear(_ animated: Bool) { let welcomeVC = WelcomeViewController() let navigationController = UINavigationController(rootViewController: welcomeVC) navigationController.isModalInPresentation = true if let sheet = navigationController.sheetPresentationController { sheet.detents = [.custom { context in return 200 }, .medium(), .large()] sheet.prefersGrabberVisible = true sheet.preferredCornerRadius = 15 sheet.largestUndimmedDetentIdentifier = .large } present(navigationController, animated: true) } } class WelcomeViewController: UIViewController { override func viewDidLoad() { let blurEffect = UIBlurEffect(style: .systemThinMaterial) let blurEffectView = UIVisualEffectView(effect: blurEffect) blurEffectView.frame = view.bounds view.addSubview(blurEffectView) view.sendSubviewToBack(blurEffectView) let button = UIButton() button.setTitle("Go to View Controller 2", for: .normal) button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false view.addSubview(button) NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.centerYAnchor.constraint(equalTo: view.centerYAnchor), button.widthAnchor.constraint(equalToConstant: 200), button.heightAnchor.constraint(equalToConstant: 44) ]) } @objc func buttonTapped() { let vc2 = ViewController2() navigationController?.pushViewController(vc2, animated: true) } } class ViewController2: UIViewController { override func viewDidLoad() { let blurEffect = UIBlurEffect(style: .systemThinMaterial) let blurEffectView = UIVisualEffectView(effect: blurEffect) blurEffectView.frame = view.bounds view.addSubview(blurEffectView) view.sendSubviewToBack(blurEffectView) let button = UIButton() button.setTitle("Go back to Welcome VC", for: .normal) button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false view.addSubview(button) NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.centerYAnchor.constraint(equalTo: view.centerYAnchor), button.widthAnchor.constraint(equalToConstant: 200), button.heightAnchor.constraint(equalToConstant: 44) ]) } @objc func buttonTapped() { navigationController?.popViewController(animated: true) } }
修复方案
问题原因
- 直接用
frame = view.bounds设置模糊视图尺寸,未适配自动布局,动画过程中视图尺寸重新计算时出现冲突 - Sheet样式的导航控制器中,导航栏默认半透明,模糊视图的渲染层级与导航动画的图层交互导致卡顿
- 模糊视图未设置
clipsToBounds,动画过程中可能出现视图溢出,影响动画完整性
具体修复步骤
- 给UIVisualEffectView添加自动布局约束,确保其始终填满父视图
- 关闭导航控制器的导航栏半透明属性,避免模糊效果与导航栏渲染冲突
- 给模糊视图设置
clipsToBounds = true,防止动画过程中视图溢出
修改后的完整代码
class ViewController: UIViewController { override func loadView() { view = MKMapView() } override func viewDidLoad() { super.viewDidLoad() } override func viewDidAppear(_ animated: Bool) { let welcomeVC = WelcomeViewController() let navigationController = UINavigationController(rootViewController: welcomeVC) // 关闭导航栏半透明,避免模糊视图渲染冲突 navigationController.navigationBar.isTranslucent = false navigationController.isModalInPresentation = true if let sheet = navigationController.sheetPresentationController { sheet.detents = [.custom { context in return 200 }, .medium(), .large()] sheet.prefersGrabberVisible = true sheet.preferredCornerRadius = 15 sheet.largestUndimmedDetentIdentifier = .large } present(navigationController, animated: true) } } class WelcomeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let blurEffect = UIBlurEffect(style: .systemThinMaterial) let blurEffectView = UIVisualEffectView(effect: blurEffect) blurEffectView.translatesAutoresizingMaskIntoConstraints = false blurEffectView.clipsToBounds = true view.addSubview(blurEffectView) view.sendSubviewToBack(blurEffectView) // 添加自动布局约束,让模糊视图填满父视图 NSLayoutConstraint.activate([ blurEffectView.topAnchor.constraint(equalTo: view.topAnchor), blurEffectView.leadingAnchor.constraint(equalTo: view.leadingAnchor), blurEffectView.trailingAnchor.constraint(equalTo: view.trailingAnchor), blurEffectView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) let button = UIButton() button.setTitle("Go to View Controller 2", for: .normal) button.setTitleColor(.systemBlue, for: .normal) button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false view.addSubview(button) NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.centerYAnchor.constraint(equalTo: view.centerYAnchor), button.widthAnchor.constraint(equalToConstant: 200), button.heightAnchor.constraint(equalToConstant: 44) ]) } @objc func buttonTapped() { let vc2 = ViewController2() navigationController?.pushViewController(vc2, animated: true) } } class ViewController2: UIViewController { override func viewDidLoad() { super.viewDidLoad() let blurEffect = UIBlurEffect(style: .systemThinMaterial) let blurEffectView = UIVisualEffectView(effect: blurEffect) blurEffectView.translatesAutoresizingMaskIntoConstraints = false blurEffectView.clipsToBounds = true view.addSubview(blurEffectView) view.sendSubviewToBack(blurEffectView) // 添加自动布局约束 NSLayoutConstraint.activate([ blurEffectView.topAnchor.constraint(equalTo: view.topAnchor), blurEffectView.leadingAnchor.constraint(equalTo: view.leadingAnchor), blurEffectView.trailingAnchor.constraint(equalTo: view.trailingAnchor), blurEffectView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) let button = UIButton() button.setTitle("Go back to Welcome VC", for: .normal) button.setTitleColor(.systemBlue, for: .normal) button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false view.addSubview(button) NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.centerYAnchor.constraint(equalTo: view.centerYAnchor), button.widthAnchor.constraint(equalToConstant: 200), button.heightAnchor.constraint(equalToConstant: 44) ]) } @objc func buttonTapped() { navigationController?.popViewController(animated: true) } }
额外说明
如果需要保留导航栏半透明效果,可以不关闭isTranslucent,而是将模糊视图的顶部约束绑定到view.safeAreaLayoutGuide.topAnchor,避免模糊视图被导航栏遮挡,同时保证动画过程中尺寸计算正确。
内容的提问来源于stack exchange,提问作者User95797654974
相关产品推荐
相关产品推荐

