Ios 如何在退出队列之前在UITableViewCell中保留用户输入
我正在创建一个应用程序,需要用户在Ios 如何在退出队列之前在UITableViewCell中保留用户输入,ios,swift,uitableview,Ios,Swift,Uitableview,我正在创建一个应用程序,需要用户在UITableViewCell中填写大量输入,有点像表单。当用户点击“完成”按钮时,我需要收集这些输入,以便运行一些计算并在另一个视图控制器上输出它们 以下是我用来收集这些输入的方法: func doneButtonTapped() { var dict = [String: Any]() for rows in 0...TableViewCells.getTableViewCell(ceilingType: node.ceilingSele
UITableViewCell
中填写大量输入,有点像表单。当用户点击“完成”按钮时,我需要收集这些输入,以便运行一些计算并在另一个视图控制器上输出它们
以下是我用来收集这些输入的方法:
func doneButtonTapped() {
var dict = [String: Any]()
for rows in 0...TableViewCells.getTableViewCell(ceilingType: node.ceilingSelected, moduleType: node.moduleSelected).count {
let ip = IndexPath(row: rows, section: 0)
let cells = tableView.cellForRow(at: ip)
if let numericCell = cells as? NumericInputTableViewCell {
if let text = numericCell.userInputTextField.text {
dict[numericCell.numericTitleLabel.text!] = text
}
} else if let booleanCell = cells as? BooleanInputTableViewCell {
let booleanSelection = booleanCell.booleanToggleSwitch.isOn
dict[booleanCell.booleanTitleLabel.text!] = booleanSelection
}
}
let calculator = Calculator(userInputDictionary: dict, ceiling_type: node.ceilingSelected)
}
我遇到的问题是,当单元格看不见时,用户的输入会从内存中清除。下面是两个屏幕截图来说明我的观点:
如您所见,当所有单元格出现时,“完成”按钮设法存储来自用户的所有输入,显然是来自控制台打印。但是,如果单元格不可见,则area/m2的输入设置为nil:
我想到的解决方案是,我不应该使用一个可出列的单元格,因为我确实希望该单元格在看不见的情况下位于内存中,但stackover社区的许多人强烈反对这种做法。我应该如何解决这个问题?谢谢
更新
cellForRow的代码(at:indepath)
我的两个自定义单元格
NumericInputTableViewCell
class NumericInputTableViewCell: UITableViewCell {
@IBOutlet weak var numericTitleLabel: UILabel!
@IBOutlet weak var userInputTextField: UITextField!
}
class BooleanInputTableViewCell: UITableViewCell {
@IBOutlet weak var booleanTitleLabel: UILabel!
@IBOutlet weak var booleanToggleSwitch: UISwitch!
}
booleanInputableViewCell
class NumericInputTableViewCell: UITableViewCell {
@IBOutlet weak var numericTitleLabel: UILabel!
@IBOutlet weak var userInputTextField: UITextField!
}
class BooleanInputTableViewCell: UITableViewCell {
@IBOutlet weak var booleanTitleLabel: UILabel!
@IBOutlet weak var booleanToggleSwitch: UISwitch!
}
任何接受者?由于表视图重用其单元格,通常,如果数据依赖于表视图单元格中的某些组件,这不是一个好主意。相反,它应该是另一种方式。即使在案例中提供任何用户输入数据之前,表视图数据始终驱动其表视图单元格的组件
- 初始数据-您的代码中应该已经有了。我根据您提供的代码创建了自己的代码
let data = CellData() data.title = "Troffer Light Fittin" data.value = false let data2 = CellData() data2.title = "Length Drop" data2.value = "0" cellData.append(data) cellData.append(data2)
- 范例
enum CellType { case numericInput, booleanInput } class CellData { var title: String? var value: Any? var cellType: CellType { if let _ = value as? Bool { return .booleanInput } else { return .numericInput } } } protocol DataCellDelegate: class { func didChangeCellData(_ cell: UITableViewCell) } class DataTableViewCell: UITableViewCell { var data: CellData? weak var delegate: DataCellDelegate? } class NumericInputTableViewCell: DataTableViewCell { let userInputTextField: UITextField = UITextField() override var data: CellData? { didSet { textLabel?.text = data?.title if let value = data?.value as? String { userInputTextField.text = value } } } override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) userInputTextField.addTarget(self, action: #selector(textDidChange(_:)), for: .editingChanged) contentView.addSubview(userInputTextField) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func textDidChange(_ textField: UITextField) { //update data and let the delegate know data is updated data?.value = textField.text delegate?.didChangeCellData(self) } //Disregard this part override func layoutSubviews() { super.layoutSubviews() textLabel?.frame.size.height = bounds.size.height / 2 userInputTextField.frame = CGRect(x: (textLabel?.frame.origin.x ?? 10), y: bounds.size.height / 2, width: bounds.size.width - (textLabel?.frame.origin.x ?? 10), height: bounds.size.height / 2) } } class BooleanInputTableViewCell: DataTableViewCell { override var data: CellData? { didSet { textLabel?.text = data?.title if let value = data?.value as? Bool { booleanToggleSwitch.isOn = value } } } let booleanToggleSwitch = UISwitch(frame: .zero) override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) booleanToggleSwitch.addTarget(self, action: #selector(toggled), for: .valueChanged) booleanToggleSwitch.isOn = true accessoryView = booleanToggleSwitch accessoryType = .none selectionStyle = .none } func toggled() { //update data and let the delegate know data is updated data?.value = booleanToggleSwitch.isOn delegate?.didChangeCellData(self) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
- 在View Controller中,您应该更新原始数据源,以便在滚动表视图时,数据源将保留右侧信息
func didChangeCellData(_ cell: UITableViewCell) { if let cell = cell as? DataTableViewCell { for data in cellData { if let title = data.title, let titlePassed = cell.data?.title, title == titlePassed { data.value = cell.data?.value } } } for data in cellData { print("\(data.title) \(data.value)") } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return cellData.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let data = cellData[indexPath.row] let cell: DataTableViewCell if data.cellType == .booleanInput { cell = tableView.dequeueReusableCell(withIdentifier: NSStringFromClass(BooleanInputTableViewCell.self), for: indexPath) as! BooleanInputTableViewCell } else { cell = tableView.dequeueReusableCell(withIdentifier: NSStringFromClass(NumericInputTableViewCell.self), for: indexPath) as! NumericInputTableViewCell } cell.data = cellData[indexPath.row] cell.delegate = self return cell }
请忽略任何与布局有关的内容。我没有使用故事板来测试您的需求。因为表视图重用其单元格,通常,如果您的数据依赖于表视图单元格中的某些组件,这不是一个好主意。相反,它应该是另一种方式。即使在案例中提供任何用户输入数据之前,表视图数据始终驱动其表视图单元格的组件
- 初始数据-您的代码中应该已经有了。我根据您提供的代码创建了自己的代码
let data = CellData() data.title = "Troffer Light Fittin" data.value = false let data2 = CellData() data2.title = "Length Drop" data2.value = "0" cellData.append(data) cellData.append(data2)
- 范例
enum CellType { case numericInput, booleanInput } class CellData { var title: String? var value: Any? var cellType: CellType { if let _ = value as? Bool { return .booleanInput } else { return .numericInput } } } protocol DataCellDelegate: class { func didChangeCellData(_ cell: UITableViewCell) } class DataTableViewCell: UITableViewCell { var data: CellData? weak var delegate: DataCellDelegate? } class NumericInputTableViewCell: DataTableViewCell { let userInputTextField: UITextField = UITextField() override var data: CellData? { didSet { textLabel?.text = data?.title if let value = data?.value as? String { userInputTextField.text = value } } } override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) userInputTextField.addTarget(self, action: #selector(textDidChange(_:)), for: .editingChanged) contentView.addSubview(userInputTextField) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func textDidChange(_ textField: UITextField) { //update data and let the delegate know data is updated data?.value = textField.text delegate?.didChangeCellData(self) } //Disregard this part override func layoutSubviews() { super.layoutSubviews() textLabel?.frame.size.height = bounds.size.height / 2 userInputTextField.frame = CGRect(x: (textLabel?.frame.origin.x ?? 10), y: bounds.size.height / 2, width: bounds.size.width - (textLabel?.frame.origin.x ?? 10), height: bounds.size.height / 2) } } class BooleanInputTableViewCell: DataTableViewCell { override var data: CellData? { didSet { textLabel?.text = data?.title if let value = data?.value as? Bool { booleanToggleSwitch.isOn = value } } } let booleanToggleSwitch = UISwitch(frame: .zero) override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) booleanToggleSwitch.addTarget(self, action: #selector(toggled), for: .valueChanged) booleanToggleSwitch.isOn = true accessoryView = booleanToggleSwitch accessoryType = .none selectionStyle = .none } func toggled() { //update data and let the delegate know data is updated data?.value = booleanToggleSwitch.isOn delegate?.didChangeCellData(self) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
- 在View Controller中,您应该更新原始数据源,以便在滚动表视图时,数据源将保留右侧信息
func didChangeCellData(_ cell: UITableViewCell) { if let cell = cell as? DataTableViewCell { for data in cellData { if let title = data.title, let titlePassed = cell.data?.title, title == titlePassed { data.value = cell.data?.value } } } for data in cellData { print("\(data.title) \(data.value)") } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return cellData.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let data = cellData[indexPath.row] let cell: DataTableViewCell if data.cellType == .booleanInput { cell = tableView.dequeueReusableCell(withIdentifier: NSStringFromClass(BooleanInputTableViewCell.self), for: indexPath) as! BooleanInputTableViewCell } else { cell = tableView.dequeueReusableCell(withIdentifier: NSStringFromClass(NumericInputTableViewCell.self), for: indexPath) as! NumericInputTableViewCell } cell.data = cellData[indexPath.row] cell.delegate = self return cell }
请忽略任何与布局有关的内容。我没有使用故事板来测试您的需求。我同意其他贡献者的观点。这些单元格不应用于数据存储。你应该考虑另一种方法(如建议的那样)。 但是,由于您的问题也是关于如何在移除UITableViewCell之前访问它,因此在UITableViewDelegate中有一种方法可用于:
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
// do something with the cell before it gets deallocated
}
此方法告诉委托指定的单元格已从表中删除。因此,它给了我们最后一次机会在细胞消失之前对其进行处理。我同意其他贡献者的观点。这些单元格不应用于数据存储。你应该考虑另一种方法(如建议的那样)。 但是,由于您的问题也是关于如何在移除UITableViewCell之前访问它,因此在UITableViewDelegate中有一种方法可用于:
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
// do something with the cell before it gets deallocated
}
此方法告诉委托指定的单元格已从表中删除。因此,在该单元格消失之前,它提供了最后一次对其进行操作的机会。为什么不在用户填充字典时填充字典呢?然后,所有数据都存储在字典中,而不是表中(不推荐使用),使用委托将数据传递回dict(数据源)。由于单元格被重用,您不希望依赖单元格上的显示数据。请同意@GIJOW。换句话说,视图不应被用作数据模型的替代品。您只需为每个单元格生成并注册不同的reuseidenfitifer,这将确保下一次相同的单元格出列。另外,只要确保在存储单元时设置了相同的内容,单元逻辑就不会刷新该单元dequeued@GIJOW我在
didSelectRow
中尝试了这个方法,但没有成功,这意味着只有当用户没有点击文本字段时,才能存储输入,这是因为直接点击文本字段,文本字段将是第一个响应的字段,而不是注册TAP。为什么不在用户填写词典时填写词典?然后,所有数据都存储在字典中,而不是表中(不推荐使用),使用委托将数据传递回dict(数据源)。由于单元格被重用,您不希望依赖单元格上的显示数据。请同意@GIJOW。换句话说,视图不应被用作数据模型的替代品。您只需为每个单元格生成并注册不同的reuseidenfitifer,这将确保下一次相同的单元格出列。另外,只要确保在存储单元时设置了相同的内容,单元逻辑就不会刷新该单元dequeued@GIJOW我在didSelectRow
中尝试了这个方法,但没有成功,这意味着只有当用户没有点击文本字段时,才能存储输入,这是因为直接点击文本字段,文本字段将是第一个响应的字段,而不是registerin