iOS TableView获取屏幕外不可见单元格数据返回nil的解决咨询
解决TableView获取屏幕外单元格数据为nil的问题
问题本质
UITableView.cellForRow(at:) 只能拿到当前在屏幕上可见的单元格,屏幕外的单元格会被系统回收复用,所以调用这个方法会返回nil——你现在的问题就是因为直接从单元格读取数据,而非从数据源拿。正确的做法是用数据驱动UI,让用户输入的内容实时同步到数据源,提交时直接从数据源取数据。
具体解决方案
1. 定义数据源模型
先做一个能存储所有单元格输入数据的模型,把每个类型的答案都对应上:
enum SurveyCellType { case yesNoCell, textWithCheckBoxesCell, textWithRadioBtnsCell, ratingCell, ratingWithTextBoxCell, appTextBoxCell, none } struct SurveyItem { let question: String? let cellType: SurveyCellType // 对应各单元格的输入数据 var yesNoAnswer: String? var checkboxAnswer: String? var radioAnswer: String? var ratingText: String? var ratingWithText: (rating: String?, text: String?)? var textboxAnswer: String? }
把你原有的eachIndexInfo数组改成[SurveyItem]类型,作为TableView的数据源。
2. 给单元格加回调,同步输入到数据源
每个自定义单元格里加个回调闭包,用户输入内容时,把数据同步回数据源。以YesNoTableViewCell为例:
class YesNoTableViewCell: UITableViewCell { var savedFlagValue: String? // 输入变化时的回调 var onAnswerChanged: ((String?) -> Void)? // 比如用户点了Yes/No按钮时调用这个方法 func selectAnswer(_ answer: String) { savedFlagValue = answer onAnswerChanged?(answer) } }
其他类型单元格同理,比如单选按钮单元格:
class TextWithRadioButtonsTableViewCell: UITableViewCell { var selectedOption: String? var onOptionSelected: ((String?) -> Void)? func optionTapped(_ option: String) { selectedOption = option onOptionSelected?(option) } }
3. 配置单元格时绑定回调
在tableView(_:cellForRowAt:)里,给每个单元格设置回调,把用户输入实时存到数据源:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let item = eachIndexInfo[indexPath.row] switch item.cellType { case .yesNoCell: let cell = tableView.dequeueReusableCell(withIdentifier: "YesNoCell", for: indexPath) as! YesNoTableViewCell // 初始化显示已保存的答案 cell.savedFlagValue = item.yesNoAnswer // 绑定回调,同步数据到数据源 cell.onAnswerChanged = { [weak self] answer in guard let self = self else { return } self.eachIndexInfo[indexPath.row].yesNoAnswer = answer } return cell case .textWithRadioBtnsCell: let cell = tableView.dequeueReusableCell(withIdentifier: "RadioCell", for: indexPath) as! TextWithRadioButtonsTableViewCell cell.selectedOption = item.radioAnswer cell.onOptionSelected = { [weak self] option in guard let self = self else { return } self.eachIndexInfo[indexPath.row].radioAnswer = option } return cell // 剩下的单元格类型都按这个逻辑处理 default: return UITableViewCell() } }
4. 提交时直接从数据源验证、取数据
修改fetchSelectedCellsInfo,不再从单元格拿数据,直接遍历数据源:
func fetchSelectedCellsInfo() -> [[String: String]]? { var result = [[String: String]]() for item in eachIndexInfo { guard let question = item.question else { continue } switch item.cellType { case .yesNoCell: guard let answer = item.yesNoAnswer, !answer.isEmpty else { showBasicAlert(title: "Please provide feedback for \(question)", message: "") return nil // 验证不通过,直接返回 } result.append(["yesNoCell.savedFlagValue": answer]) case .textWithCheckBoxesCell: guard let answer = item.checkboxAnswer, !answer.isEmpty else { showBasicAlert(title: "Please provide feedback for \(question)", message: "") return nil } result.append(["savedFlagValue": answer]) case .ratingWithTextBoxCell: guard let (rating, text) = item.ratingWithText, !rating.isEmpty, !text.isEmpty else { showBasicAlert(title: "Please provide feedback for \(question)", message: "") return nil } result.append(["savedFlagValue": rating, "textboxValue": text]) // 其他类型单元格的验证和数据读取逻辑同上 default: continue } } return result }
然后修改提交按钮的逻辑:
@IBAction func submitBtnAction(_ sender: Any) { guard let data = fetchSelectedCellsInfo() else { // 已经弹出提示,无需额外操作 return } // 这里调用API传递data }
额外提醒
你原代码里混用了appSurveyTableview和myTableview,要确保操作的是同一个TableView实例,避免不必要的错误。
内容的提问来源于stack exchange,提问作者Anilkumar iOS Developer
相关产品推荐
相关产品推荐

