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

iOS16 UIKit中NavigationController跳转时旧视图短暂闪现BUG问询

iOS 16 UIKit NavigationController 跳转时B视图短暂闪现BUG排查求助

跳转流程

A(集成UISearchController的ViewController)
-> B(搜索结果展示ViewController,仅在搜索结果出现时显示)
-> C1(搜索结果详情页,通过B的代理方法调用A的NavigationController推入;B无NavigationController,会处于C1视图层级下方,从C1返回时可见)
-> C2(C1自行推入的同类型详情页)

行为差异

  • 预期:C1推入C2时,直接展示C2视图
  • 实际:C1推入C2过程中,B视图会短暂闪现,随后才切换到C2;该行为无代码逻辑触发

复现规律

  • 随机触发:约每40-50次搜索出现1次,同一搜索结果第二次操作不会触发
  • 持续性:一旦在某个C1视图上触发,后续从该C1推入C2会持续出现BUG
  • 可恢复:从C1返回至B,再重新进入该C1,BUG不再出现

已完成的排查

  • 控制台无错误、警告输出
  • 推入C2前断点验证:初始化所需数据已完全就绪
  • 生命周期钩子断点追踪:B视图在其viewWillAppear执行后闪现,持续到C2的viewDidLoad完成后才切换到C2;期间C2视图似乎未完成加载
  • 添加init/deinit日志:所有ViewController的分配、释放均正常,无内存泄漏或视图保留问题
  • Xcode UI层级捕获:BUG触发时仅能抓到C2的正确层级,无法捕获到闪现的B视图
  • Profiler内存分析:内存使用随操作正常波动,返回A后恢复至约30MB,无异常

补充说明

  • 仅在iOS 16真机(iPhone 12)上出现,iOS 16模拟器无此问题
  • 已完全移除Storyboard,采用纯代码实现界面
  • 排除数据加载问题:推入无动态数据的静态ViewController D时,同样会触发该BUG
  • 经过数周测试,仍无法稳定复现该BUG

代码简化示例

View A (SearchViewController)

class SearchViewController: UIViewController, UISearchResultsUpdating {
    let searchController: UISearchController = {
        let vc = UISearchController(searchResultsController: SearchResultsViewController())
        vc.definesPresentationContext = true
        return vc
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        searchController.searchResultsUpdater = self
    }
    
    func updateSearchResults(for searchController: UISearchController) {
        let result = [] // 实际为搜索结果数组
        guard let resultsController = self.searchController.searchResultsController as? SearchResultsViewController,
              let query = searchController.searchBar.text, !query.isEmpty else {
            return
        }
        resultsController.delegate = self
        resultsController.update(with: result)
    }
}

extension SearchViewController: SearchResultsViewControllerDelegate {
    func didTapResult(_ result: Record) {
        let vc = RecordDetailViewController(rec: result)
        navigationController?.pushViewController(vc, animated: true)
    }
}

View B (SearchResultsViewController)

class SearchResultsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    weak var delegate: SearchResultsViewControllerDelegate?
    private let tableView: UITableView = {
        let tableView = UITableView()
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "myCell")
        return tableView
    }()
    private var filteredData: [Record] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(tableView)
        tableView.delegate = self
        tableView.dataSource = self
        tableView.keyboardDismissMode = .onDrag
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        tableView.frame = view.bounds
    }
    
    func update(with results: [Record]) {
        filteredData = results
        tableView.reloadData()
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        delegate?.didTapResult(filteredData[indexPath.row])
    }
    
    // 省略其他UITableView代理方法
}

protocol SearchResultsViewControllerDelegate: AnyObject {
    func didTapResult(_ result: Record)
}

View C (RecordDetailViewController)

class RecordDetailViewController: UIViewController {
    private let rec: Record
    private let tableView = UITableView()
    
    init(rec: Record) {
        self.rec = rec
        super.init(nibName: nil, bundle: nil)
        print("alloced")
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit {
        print("dealloced")
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationItem.largeTitleDisplayMode = .never
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configure()
        view.addSubview(tableView)
        tableView.delegate = self
        tableView.dataSource = self
        tableView.frame = view.bounds
    }
    
    private func configure() {
        // 省略视图配置逻辑
    }
}

extension RecordDetailViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let rec = Record() // 实际为详情页关联的记录
        let vc = RecordDetailViewController(rec: rec)
        navigationController?.pushViewController(vc, animated: true)
    }
    
    // 省略其他UITableView代理方法
}

表现对比

问题表现

问题表现

预期表现

预期表现


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 00:05:32