【问题标题】:Swift: Retrieving values out of TableView TextfieldsSwift:从 TableView 文本字段中检索值
【发布时间】:2020-04-27 13:56:14
【问题描述】:

我只是试图从我的 TableView 中检索我的所有文本字段值。它适用于 11 个案例中的 10 个。我尝试了以下方法:

    let journeyIDTextField = tableView.cellForRow(at: NSIndexPath(row: 0, section: 1) as IndexPath) as! InputTableViewCell
    journeyID = journeyIDTextField.cellInputTextfield.text!

当我将部分从 1-10 更改为一切正常时,部分 0 会导致错误。 致命错误:在展开可选值时意外发现 nil

因此我尝试查看 IndexPath (0,0) 处是否有文本字段。

    print(section.description, indexPath.row, indexPath.section)
    Result: Description 0 0

所以在 0,0 处肯定有一个文本字段。我不知道该怎么做,尤其是因为它在另一个 ViewController 上运行良好。

有什么想法吗?

最好, 蒂莫

func numberOfSections(in tableView: UITableView) -> Int {
    return JourneySection.allCases.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifierInputCell, for: indexPath) as! InputTableViewCell

    guard let section = JourneySection(rawValue: indexPath.section) else { return UITableViewCell() }
    cell.cellInputTextfield.placeholder = section.description
    print(section.description, indexPath.row, indexPath.section)

    return cell
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 1
}

这是我的牢房: 导入 UIKit

class InputTableViewCell: UITableViewCell {

let cellInputTextfield: UITextField = {
    let cellInputTextfield = UITextField()
    cellInputTextfield.textColor = .black
    cellInputTextfield.sizeToFit()
    return cellInputTextfield
}()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: .default, reuseIdentifier: reuseIdentifier)

    cellInputTextfield.frame = CGRect(x: 20, y: 0, width: self.frame.width, height: 60)
    cellInputTextfield.font = UIFont.systemFont(ofSize: 15)
    self.contentView.addSubview(cellInputTextfield)


}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

}

enum JourneySection:Int, CaseIterable, CustomStringConvertible{
case Description
case ID
case Confirmation
case Destination
case DestinationDate
case DestinationTime
case Arrival
case ArrivalDate
case ArrivalTime
case PriceTotal
case Companions


var description: String {
    switch  self {
    case .Description: return "Description"
    case .ID: return "ID f.ex. Flight Number"
    case .Confirmation: return "Confirmation No."
    case .Destination: return "Destination"
    case .DestinationDate: return "Destination Date, like DD-MM-YYYY"
    case .DestinationTime: return "Destination Time, like hh-mm"
    case .Arrival: return "Arrival"
    case .ArrivalDate: return "Arrival Date, like DD-MM-YYYY"
    case .ArrivalTime: return "Arrival Time, like hh-mm"
    case .PriceTotal: return "Total Price"
    case .Companions: return " No. Of Companions"
    }

}

}

【问题讨论】:

  • 根据您分享的有限信息,很难说。但贴出以下方法:tableView(_:numberOfRowsInSection:)numberOfSections(in:)tableView(_:cellForRowAt:)`
  • 更新了,我的错

标签: ios swift uitableview


【解决方案1】:

我认为一个问题是当 journeyIDTextField.cellInputTextfield.text 等于 nil 时,你会强制解包。我看到的另一个潜在问题是,由于单元格重用,当您滚动时,您的文本字段文本将被擦除。要在重复使用的单元格中正确使用文本字段,请参阅this question

【讨论】:

    【解决方案2】:

    您永远不想“从单元格中获取文本”。单元格被重复使用,因此当您滚动 位于 Section: 0 Row: 0 中的文本字段时,现在可能位于 Section: 10 Row: 0 中。

    改为在cellForRowAt 中为您的单元格分配一个“回调闭包”。当用户编辑文本字段时,让您的单元格“回调”到控制器以更新数据源。

    这是一个完整的示例,您的代码稍作修改:

    class InputTableViewCell: UITableViewCell {
    
        // callback closure to tell the controller the text field was edited
        var callback: ((String) ->())?
    
        let cellInputTextfield: UITextField = {
            let cellInputTextfield = UITextField()
            cellInputTextfield.textColor = .black
            cellInputTextfield.sizeToFit()
            return cellInputTextfield
        }()
    
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: .default, reuseIdentifier: reuseIdentifier)
    
            cellInputTextfield.frame = CGRect(x: 20, y: 0, width: self.frame.width, height: 60)
            cellInputTextfield.font = UIFont.systemFont(ofSize: 15)
            self.contentView.addSubview(cellInputTextfield)
    
            // add a target func to call when the text field is edited
            cellInputTextfield.addTarget(self, action: #selector(textFieldChanged(_:)), for: .editingChanged)
        }
    
        @objc func textFieldChanged(_ textField: UITextField) -> Void {
            // end the edited text back to the controller
            callback?(textField.text ?? "")
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
    enum JourneySection:Int, CaseIterable, CustomStringConvertible{
        case Description
        case ID
        case Confirmation
        case Destination
        case DestinationDate
        case DestinationTime
        case Arrival
        case ArrivalDate
        case ArrivalTime
        case PriceTotal
        case Companions
    
    
        var description: String {
            switch  self {
            case .Description: return "Description"
            case .ID: return "ID f.ex. Flight Number"
            case .Confirmation: return "Confirmation No."
            case .Destination: return "Destination"
            case .DestinationDate: return "Destination Date, like DD-MM-YYYY"
            case .DestinationTime: return "Destination Time, like hh-mm"
            case .Arrival: return "Arrival"
            case .ArrivalDate: return "Arrival Date, like DD-MM-YYYY"
            case .ArrivalTime: return "Arrival Time, like hh-mm"
            case .PriceTotal: return "Total Price"
            case .Companions: return "No. Of Companions"
            }
    
        }
    
    }
    
    class JourneyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
        let testButton: UIButton = {
            let v = UIButton()
            v.translatesAutoresizingMaskIntoConstraints = false
            v.setTitle("Check Data", for: [])
            v.setTitleColor(.white, for: [])
            v.backgroundColor = .red
            return v
        }()
    
        let tableView: UITableView = {
            let v = UITableView()
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
    
        let reuseIdentifierInputCell = "journeyCell"
    
        // declare a string data array
        var dataStrings: [String] = [String]()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // initialize the data array with an empty string for each case
            // in actual use, you may have already populated "saved" data
            dataStrings = Array(repeating: "", count: JourneySection.allCases.count)
    
            // add the button and table view
            view.addSubview(testButton)
            view.addSubview(tableView)
    
            // respect safe area
            let g = view.safeAreaLayoutGuide
    
            NSLayoutConstraint.activate([
    
                // constrain the button to Top: 20-pts and centered horizontally
                testButton.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
                testButton.centerXAnchor.constraint(equalTo: g.centerXAnchor),
    
                // constrain the tableview to 8-pts below the button
                // Leading / Trailing at 20-pts
                // with a height of 240 (so we can see what happens when scrolling)
                tableView.topAnchor.constraint(equalTo: testButton.bottomAnchor, constant: 8.0),
                tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
                tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
                tableView.heightAnchor.constraint(equalToConstant: 240.0),
    
            ])
    
            // register the cell class
            tableView.register(InputTableViewCell.self, forCellReuseIdentifier: reuseIdentifierInputCell)
    
            // set dataSource and delegate
            tableView.dataSource = self
            tableView.delegate = self
    
            // dismiss keyboard when table scrolls
            tableView.keyboardDismissMode = .onDrag
    
            testButton.addTarget(self, action: #selector(showData(_:)), for: .touchUpInside)
        }
    
        @objc func showData(_ sender: UIButton) -> Void {
            for i in 0..<JourneySection.allCases.count {
                guard let section = JourneySection(rawValue: i) else {
                    fatalError("Something wrong with JourneySection")
                }
                print(section.description, ":", dataStrings[i])
            }
        }
    
        func numberOfSections(in tableView: UITableView) -> Int {
            return JourneySection.allCases.count
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifierInputCell, for: indexPath) as! InputTableViewCell
    
            guard let section = JourneySection(rawValue: indexPath.section) else { return UITableViewCell() }
    
            // set placeholder
            cell.cellInputTextfield.placeholder = section.description
    
            // set the cell's text field's text
            // if this entry in our data source is "", the placeholder will be shown
            cell.cellInputTextfield.text = dataStrings[indexPath.section]
    
            // we want the cell to "call us back" when the textfield is edited
            cell.callback = { str in
                // update our data source
                self.dataStrings[indexPath.section] = str
            }
    
            return cell
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 1
        }
    
    }
    

    当你运行它时,你应该得到:

    您可以在字段中输入文本...上下滚动...当您点击“检查数据”按钮时,您将看到枚举属性列表和字段中保存的数据。

    【讨论】: