iOS16 UIKit中NavigationController跳转时旧视图短暂闪现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
相关产品推荐
相关产品推荐

