Swift开发调用didMove(toParent)后tableView不显示且空值报错如何解决
问题描述
WalletViewController中的tableView始终无法显示,导航栏按钮也不生效,怀疑问题和调用didMove(toParent)方法添加子控制器的逻辑相关。需要实现一个可向个人资料添加新银行卡的页面,无论是纯代码编写还是使用storyboard实现,都会抛出错误:Unexpectedly found nil while implicitly unwrapping an Optional value。
问题根因
- 子控制器缺少导航栏上下文:WalletViewController作为子控制器被直接添加到BeginViewController的视图上,本身没有嵌套在UINavigationController中,导致
navigationItem、navigationController均为nil,导航栏按钮无法生效,调用导航控制器相关方法时还会触发隐式解包崩溃。 - 表格数据未刷新:从UserDefaults读取银行卡数据后,没有调用
tableView.reloadData()刷新表格,导致tableView始终无内容展示。 - 硬编码布局适配问题:Add按钮的frame被硬编码为y=700,小屏设备上会超出可视区域,且tableView没有做自适应约束,父视图大小变化时会出现布局错位。
- 控制器初始化逻辑冲突:加卡页addCardViewController同时存在storyboard初始化和纯代码初始化两种逻辑,如果addCardViewController是用storyboard搭建的,纯代码init会导致内部IBOutlet为nil,触发崩溃。
修复步骤
- 给所有功能子控制器套上导航容器,修改BeginViewController中的子控制器声明:
// 将原有声明替换为以下格式,其他Profile、MyBookings等控制器同理套UINavigationController private let WalletController = UINavigationController(rootViewController: WalletViewController())
- 修改WalletViewController的viewDidLoad逻辑,加载数据后刷新表格:
override func viewDidLoad() { super.viewDidLoad() addTable() // 新增两行代码,加载已存储的银行卡数据并刷新表格 updateCard() tableView.reloadData() view.backgroundColor = UIColor(named: "RED") button.setTitle("Add New Card", for: .normal) button.backgroundColor = UIColor(named: "yellow-2") button.setTitleColor(UIColor(named: "RED"), for: .normal) button.layer.cornerRadius = 15 button.addTarget(self, action: #selector(didTapAddButton), for: .touchUpInside) // 不需要导航栏右上角按钮可直接删除以下逻辑,避免nil风险 if !UserDefaults().bool(forKey: "setup") { UserDefaults().set(true, forKey: "setup") UserDefaults().set(0, forKey: "count") navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Add New Card", style: .plain, target: self, action: #selector(didTapAdd)) } }
- 替换硬编码frame为自动布局,适配不同屏幕尺寸:
func addTable() { tableView.translatesAutoresizingMaskIntoConstraints = false tableView.delegate = self tableView.dataSource = self tableView.separatorStyle = .singleLine tableView.register(UITableViewCell.self, forCellReuseIdentifier: "addCard") self.view.addSubview(tableView) button.translatesAutoresizingMaskIntoConstraints = false view.addSubview(button) NSLayoutConstraint.activate([ tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), tableView.bottomAnchor.constraint(equalTo: button.topAnchor, constant: -20), button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 25), button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -25), button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20), button.heightAnchor.constraint(equalToConstant: 50) ]) }
- 优化updateCard方法,避免重复加载数据,加载完成后自动刷新表格:
func updateCard() { addNewCard.removeAll() // 清空旧数据避免重复 guard let count = UserDefaults().value(forKey: "count") as? Int else { return } for x in 0..<count { if let addCard = UserDefaults().value(forKey: "addCard\(x+1)") as? String { addNewCard.append(addCard) } } tableView.reloadData() // 自动刷新表格 }
- 统一addCardViewController的初始化方式,要么全部用storyboard初始化,要么全部用纯代码初始化,避免混合使用导致IBOutlet隐式解包崩溃。
内容的提问来源于stack exchange,提问作者Charles Morgan
相关产品推荐
相关产品推荐

