0

I have a UITableView with dynamic cells (in previous View are sliders to create these cells). Each row contains two TextFields. First is distance from start. Second is description.

Screen TableView

I can access these textFields through a cell in tableView. My tableView:

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

    let cellIdentifier = "Cell"
    let cell: FirstAddPointTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! FirstAddPointTableViewCell

    cell.numberOfCell.text? = "\(indexPath.row + 1)."

    cell.distance.text? = arrayTextField1[indexPath.row]
    cell.description.text? = arrayTextField2[indexPath.row]

    cell.distance.tag = indexPath.row
    cell.description.tag = indexPath.row

    cell.distance.delegate = self
    cell.description.delegate = self

    return cell

I have in my code func textFieldDidEndEditing but I don't know how to access the textFields - distance and description to save the right values to my two arrays.

I know that this code is ok for only one textField. If I have two textFields this code is wrong:

func textFieldDidEndEditing(_ textField: UITextField) {
    print("End editing!")

    if textField.text != "" {
       arrayTextField1[textField.tag] = textField.text!
       arrayTextField2[textField.tag] = textField.text!


    } else if textField.text == "" {
        arrayTextField1[textField.tag] = textField.text!
        arrayTextField2[textField.tag] = textField.text!
        }
       }

There is my FirstAddPointTableViewCell:

import UIKit

class FirstAddPointTableViewCell: UITableViewCell {

    @IBOutlet weak var cislovaniPrekazek: UILabel!

    @IBOutlet weak var prekazkyFormulare: UITextField!

    @IBOutlet weak var poznamkyFormulare: UITextField!


    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
}

My "idea" is something like this code (in textFieldDidEndEditing) but I don't know how to do it. I can't gain access to them:

   arrayTextField1[distance.tag] = distance.text!
   arrayTextField2[description.tag] = description.text!

Can you help me please? Sorry for my English.

danday74
  • 52,471
  • 49
  • 232
  • 283
spread7
  • 43
  • 5

1 Answers1

1

It doesn't seem right that you're trying to set both arrayTextField1 and arrayTextField2 during a single call to textFieldDidEndEditing(). You need to associate the textField that was edited to the correct array.

One thought is to encode extra information in the tag of each UITextField. All you need is something like below, where we leverage fact that tag is a signed integer:

    // In your tableView cellForRowAt method edit the tag setting lines as such:
    cell.distance.tag = indexPath.row + 1 // tag will be 1-based since -0 == 0
    cell.description.tag = -(indexPath.row + 1)  // store it as a negative row so we can distinguish description text fields later

    func textFieldDidEndEditing(_ textField: UITextField) 
    {
        guard let text = textField.text else { return }

        if textField.tag > 0 {
           arrayTextField1[textField.tag - 1] = text
        } else if textField.tag < 0 {
           arrayTextField2[abs(textField.tag) - 1] = text
        } else {
           assert(true) // some other text field ended editing?
        }
    }

And if you go with the above approach, be sure to encapsulate the tag calculations in some functions to be extra clear for future maintainability.

A more elegant solution for attaching any amount of data to a UIView without subclassing it is here.

user120242
  • 14,918
  • 3
  • 38
  • 52
Smartcat
  • 2,834
  • 1
  • 13
  • 25
  • Thank you for your time and help. Assert(0) - there is error - Cannot convert value of type Int to Bool... So I tried it - first textField is OK but the second on the same line is after scroll tableView down and up on the third row. – spread7 Aug 13 '17 at 17:37
  • I changed the assert and now the second textField (description) is empty. There is gif - https://media.giphy.com/media/l41K5ueOtmfurYa08/giphy.gif – spread7 Aug 13 '17 at 17:48
  • The whole idea of storing UITextFields in an array beyond the life of the cell is wrong. Create them after each dequeue of a cell, and set their text from an array of data. My tag approach will still let you match up the edited UITextField with the data arrays, for both columns. I could edit my answer for this, but really it would need a whole new question because it would go beyond the scope of this question about matching up to existing array of textFields. – Smartcat Aug 13 '17 at 18:01
  • My program works in this way: users fill the name of route, total distance and the number of row in tableviews. After this - tap Next. Then user fill in every line distance from start (something like checkpoint) and description in textfield. So it is not possible to load data from array because it is empty and It is necessary to put data into Array via textField. Could you give me advice or the problem is that I try to solve it from bad point of view? – spread7 Aug 13 '17 at 20:18
  • I know that if user fill first all distances (checkpoints) - save and then in new tableView all descriptions.. But it's long way for user I think. – spread7 Aug 13 '17 at 20:36
  • You don't have to change your UI plan. Just plan out your model classes independently from any UI. Don't use the UI controls to act as your model classes. So sounds like you'd have a Route class with a name, distance, and an array of Checkpoint classes. A Checkpoint has a distance and a description. Now once the user has tapped Next on the first screen, create the Route and set its name and distance from the UI controls. Also populate the array with as many Checkpoints as user chose. Use a default 0 distance and empty description for each Checkpoint. – Smartcat Aug 13 '17 at 23:56
  • Now your second viewController can simply use the count of Checkpoints for tableView's numberOfRows, and anytime the tableView wants a cell, dequeue one. Your custom cell class should create (or specify in a .xib) the two UITextField controls, and hold a weak ref to them. When the tableView asks for a cell and you dequeue one, populate the cell's two textField text properties by consulting the Route's Checkpoints array, using indexPath.row of the cell. – Smartcat Aug 14 '17 at 00:03
  • Then use my answer above for tagging the two textFields. Or, here's an even simpler solution: just set all the distance textFields' tag to 1, and all the description textFields' tag to 2. Then in the scrollView delegate endEditing call, simply cast the edited textField's superview to a UITextViewCell and ask your tableView what index in the table that cell is at. And look at the tectField's tag to know it it's a distance or description. :). Do this and you'll be off to the races, or, er, onto yer route! :) – Smartcat Aug 14 '17 at 00:11
  • Thank you so much for your advices!! :-) ..I have to think about it! – spread7 Aug 14 '17 at 02:31
  • @Smartcat, the solution you gave is nice, but the close parenthesis is at the wrong position which was making the calculation buggy, minus 1 before getting the abs is actually adding the minus values and then making its abs, so need to get the abs value first then minus 1, I've edited the answer please accept :) – Mohit G. Apr 09 '20 at 16:01