UIAlertController内嵌UITableView移入独立类后无法加载数据问题
解决UITableView在UIAlertController中不显示行的问题
你遇到的问题主要有两个核心原因,我帮你逐一梳理并给出修复方案:
1. 未禁用UITableView的自动约束转换
当使用Auto Layout给视图添加自定义约束时,必须将视图的translatesAutoresizingMaskIntoConstraints属性设为false。否则系统会根据你初始化时的frame自动生成默认约束,这会和你手动添加的约束产生冲突,最终导致UITableView被压缩到没有高度——虽然numberOfRowsInSection返回了正确的行数,但因为tableView本身不可见,系统不会触发cellForRowAt方法。
2. 未正确传递数据源到ListPicker实例
你的showListPicker方法接收了items参数,但没有把它赋值给类的self.items属性。这会导致tableView(_:numberOfRowsInSection:)方法中items?.count实际为nil(除非你在外部提前给ListPicker的items属性赋值,但这不符合封装逻辑),即使你认为返回了正确行数,也可能是测试时的偶然情况。
修改后的完整代码
我标注了关键修改点,同时做了一些体验优化:
class ListPicker: UIViewController, UITableViewDelegate, UITableViewDataSource { //MARK: Variables var items: [String]? // 新增:保存选中回调,避免循环引用风险 private var itemSelectedCallback: ((String) -> ())? //MARK: ViewController Events override func viewDidLoad() { super.viewDidLoad() } //MARK: TableView Events func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items?.count ?? 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! cell.textLabel!.text = items![indexPath.row] return cell } // 新增:处理行选中事件,提升交互体验 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) if let selectedItem = items?[indexPath.row] { itemSelectedCallback?(selectedItem) self.dismiss(animated: true) } } func numberOfSections(in tableView: UITableView) -> Int { return 1 } //MARK: Support Methods func showListPicker( viewController: UIViewController, title: String? = "Select Item", message: String? = nil, items: [String]?, itemSelected: (@escaping (_ item: String) -> ()) ) { // 修复2:将传入的items赋值给实例属性,确保数据源正确 self.items = items self.itemSelectedCallback = itemSelected let alert = UIAlertController( title: title, message: message, preferredStyle: .alert ) // 移除初始化frame,改用约束控制尺寸 let tableView = UITableView(style: .plain) tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") tableView.delegate = self tableView.dataSource = self tableView.rowHeight = 35 tableView.backgroundColor = .white tableView.separatorInset.right = 15 tableView.bounces = false tableView.layoutMargins = .init(top: 0, left: 0, bottom: 0, right: 0) tableView.layer.cornerRadius = 10 // 修复1:禁用自动约束转换,确保自定义约束生效 tableView.translatesAutoresizingMaskIntoConstraints = false alert.view.addSubview(tableView) // 更安全的方式获取alert内容容器,避免依赖subviews索引的不稳定问题 guard let contentView = alert.view.subviews.first?.subviews.first else { return } // 批量激活约束,代码更简洁 NSLayoutConstraint.activate([ tableView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 80), tableView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -60), tableView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10), tableView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10), // 添加最小高度约束,确保数据较少时tableView也能正常显示 tableView.heightAnchor.constraint(greaterThanOrEqualToConstant: 100) ]) let okAction = UIAlertAction(title: "OK", style: .default) { [weak self] _ in if let row = tableView.indexPathForSelectedRow?.row, let selectedItem = self?.items?[row] { itemSelected(selectedItem) } } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) alert.addAction(okAction) alert.addAction(cancelAction) viewController.present(alert, animated: true) } }
额外优化说明
- 移除了message中的手动换行,改用约束控制tableView位置,避免不同设备上的显示异常
- 替换了依赖
alert.view.subviews[0]的不安全写法,改用更稳定的内容容器获取方式 - 新增了行选中自动关闭弹窗的逻辑,提升用户交互体验
- 添加了tableView最小高度约束,确保数据量少时也能正常展示
内容的提问来源于stack exchange,提问作者Joel S.
相关产品推荐
相关产品推荐

