You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UINavigationController推送新视图控制器时动画卡顿异常

修复UINavigationController推送带UIVisualEffectView的ViewController时的动画异常

问题描述

我希望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)
    }
}

修复方案

问题原因

  1. 直接用frame = view.bounds设置模糊视图尺寸,未适配自动布局,动画过程中视图尺寸重新计算时出现冲突
  2. Sheet样式的导航控制器中,导航栏默认半透明,模糊视图的渲染层级与导航动画的图层交互导致卡顿
  3. 模糊视图未设置clipsToBounds,动画过程中可能出现视图溢出,影响动画完整性

具体修复步骤

  1. 给UIVisualEffectView添加自动布局约束,确保其始终填满父视图
  2. 关闭导航控制器的导航栏半透明属性,避免模糊效果与导航栏渲染冲突
  3. 给模糊视图设置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 20:35:03