如何在出列之前保留UITableViewCell中的用户输入

时间:2017-09-21 17:19:56

标签: ios swift uitableview

我正在创建一个应用程序,我需要用户在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)
}

我遇到的问题是当单元格不在视图范围内时,用户的输入将从内存中清除。这里有两个截图来说明我的观点:

enter image description here

正如您所看到的,当所有单元格出现时,完成按钮设法存储来自用户的所有输入,显然是从控制台打印。但是,如果单元格不在视图范围内,则面积/ m2的输入将设置为nil:

enter image description here

我想到的解决方案是我不应该使用一个可以出列的单元格,因为我希望单元格在视野之外时可以在内存中,但许多堆叠社区强烈反对这种做法。我该如何解决这个问题?谢谢!

更新

cellForRow(at: IndexPath)的代码

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        guard let node = node else {
            return UITableViewCell()
        }

        let cellArray = TableViewCells.getTableViewCell(ceilingType: node.ceilingSelected, moduleType: node.moduleSelected)

        switch cellArray[indexPath.row].cellType {

        case .numericInput :

            let cell = tableView.dequeueReusableCell(withIdentifier: "numericCell", for: indexPath) as! NumericInputTableViewCell
            cell.numericTitleLabel.text = cellArray[indexPath.row].title
            return cell

        case .booleanInput :

            let cell = tableView.dequeueReusableCell(withIdentifier: "booleanCell", for: indexPath) as! BooleanInputTableViewCell
            cell.booleanTitleLabel.text = cellArray[indexPath.row].title
            return cell

        }
    }
}

我的两个自定义单元格

NumericInputTableViewCell

class NumericInputTableViewCell: UITableViewCell {

    @IBOutlet weak var numericTitleLabel: UILabel!

    @IBOutlet weak var userInputTextField: UITextField!

}

BooleanInputTableViewCell

class BooleanInputTableViewCell: UITableViewCell {

    @IBOutlet weak var booleanTitleLabel: UILabel!

    @IBOutlet weak var booleanToggleSwitch: UISwitch!

}

任何参赛者?

2 个答案:

答案 0 :(得分:2)

我同意其他贡献者的观点。不应将单元用于数据存储。您应该考虑另一种方法(如HMHero建议的那种方法)。

但是,由于您的问题还在于如何在删除之前访问UITableViewCell,因此您可以使用 UITableViewDelegate 中的方法:

func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    // do something with the cell before it gets deallocated
}

此方法告诉委托指定的单元格已从表中删除。所以它给了最后一次机会在它消失之前对那个细胞做了一些事情。

答案 1 :(得分:0)

由于表视图重用其单元格,通常,如果您的数据依赖于表格视图单元格中的某些组件,则不是一个好主意。相反,它应该是相反的方式。即使在您的案例中提供任何用户输入数据之前,您的表格视图数据也总是驱动它的表格视图单元格组件。

  • 初始数据 - 您的代码应该已存在。我从您提供的代码中创建了自己的代码

        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
    }
    

简而言之,尝试为表视图提供单个数据源,并使用委托将单元格中的更新数据传递回数据源。

请忽略与布局有关的任何事情。我没有使用故事板来测试你的要求。