0

I just tried to retrieve all my textfield values out of my TableView. It worked for 10 of 11 cases. I tried the following:

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

When I change the section from 1-10 everything works, section 0 results to an error. Fatal error: Unexpectedly found nil while unwrapping an Optional value

Therefore I tried to see if there is a textfield at IndexPath (0,0).

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

So there is definitely a textfield at 0,0. I have no idea what to do, especially because it worked fine on another ViewController.

Any ideas?

Best, Timo

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
}

And this is my cell: import 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"
    }

}

}
  • It's hard to say based on the limited information you shared. But post the following methods: `tableView(_:numberOfRowsInSection:)`, `numberOfSections(in:)` and `tableView(_:cellForRowAt:)` ` – Rob C Apr 27 '20 at 14:00
  • Updated, my bad – Stanely2206 Apr 27 '20 at 14:10

2 Answers2

0

I think one issue is that you're force unwrapping journeyIDTextField.cellInputTextfield.text when it equals nil. One other potential issue I see is that your textfield text will get wiped when you scroll due to cell reuse. For properly using a textfield in a reused cell, see this question.

Daniel
  • 3,188
  • 14
  • 34
0

You never want to "get text from a cell". Cells are reused, so when you scroll the text field that was in Section: 0 Row: 0 may now be in Section: 10 Row: 0 .

Instead, assign a "callback closure" to your cell in cellForRowAt. When the user edits the text field, have your cell "call back" to the controller to update the data source.

Here is a complete example, with your code slightly modified:

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
    }

}

When you run it, you should get:

enter image description here

You can enter text in the fields... scroll up and down... and when you tap the "Check Data" button you'll see your list of enum properties and the saved data from the fields.

DonMag
  • 69,424
  • 5
  • 50
  • 86