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

将带inputAccessoryView的UIViewController推入导航栈时动画闪烁异常

解决UINavigationController Push时inputAccessoryView闪烁异常问题

这个闪烁问题是UIKit在push动画过程中的常见行为:动画执行时,新控制器的inputAccessoryView会被临时附加到当前窗口,导致在父控制器界面短暂显示,动画结束后才会正确关联到新控制器的视图层级,从而产生闪烁滞后的现象。

解决方案:延迟成为第一响应者

核心思路是避免在push动画完成前让ChatViewController成为第一响应者,等动画结束后再触发inputAccessoryView的加载显示。

修改后的ChatViewController代码:

class ChatViewController: UIViewController {
    let accessoryView = UIView()
    private var allowFirstResponder = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        // 用AutoLayout替代frame设置尺寸,布局更稳定
        accessoryView.translatesAutoresizingMaskIntoConstraints = false
        accessoryView.backgroundColor = .red
        
        NSLayoutConstraint.activate([
            accessoryView.heightAnchor.constraint(equalToConstant: 80)
        ])
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // 动画完成后允许成为第一响应者并触发
        allowFirstResponder = true
        becomeFirstResponder()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // 返回时取消第一响应者状态,避免异常
        resignFirstResponder()
        allowFirstResponder = false
    }
    
    override var canBecomeFirstResponder: Bool {
        allowFirstResponder
    }

    override var inputAccessoryView: UIView? {
        accessoryView
    }
}

补充方案:通过导航控制器代理控制

如果需要更精准控制时机,可以在父控制器中设置UINavigationControllerDelegate,在push动画完全结束后再让ChatViewController成为第一响应者:

// 父控制器中实现代理
extension ParentViewController: UINavigationControllerDelegate {
    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        guard let chatVC = viewController as? ChatViewController else { return }
        chatVC.becomeFirstResponder()
    }
}

// 此时ChatViewController的canBecomeFirstResponder直接返回true即可
class ChatViewController: UIViewController {
    let accessoryView = UIView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        accessoryView.translatesAutoresizingMaskIntoConstraints = false
        accessoryView.backgroundColor = .red
        
        NSLayoutConstraint.activate([
            accessoryView.heightAnchor.constraint(equalToConstant: 80)
        ])
    }
    
    override var canBecomeFirstResponder: Bool {
        true
    }

    override var inputAccessoryView: UIView? {
        accessoryView
    }
}

关键注意点

  • 尽量使用AutoLayout约束inputAccessoryView的尺寸,避免frame布局在视图层级变化时出现的位置偏差
  • 在视图消失时务必调用resignFirstResponder(),防止返回父控制器时出现残留的inputAccessoryView

内容的提问来源于stack exchange,提问作者dfmuir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 13:47:30