1

I have the exact same scenario as in this question:

UIDatePicker in UITextField in UITableView realtime update

In summary: I have a textfield in a custom cell which calls a UIDatePicker. I want the textfield to update whenever the datepicker is modified, and I want it to store the last value when the "Done" button (located inside the toolbar created for the datepicker) is tapped.

I can get the value selected in the pickerview (I actually print it to the console), but I'm not able to put that value in the textfield.

However, I need to do it in swift, and I'm not able to. I've been trying to use action: #selector() but since it doesn't accept more than 1 parameter in the function inside the selector, it's not helping me.

Custom Table View Cell:

class employeesTableViewCell : UITableViewCell {
   @IBOutlet weak var employeeNumberField: UILabel!
   @IBOutlet weak var employeeNameField: UITextField!
   @IBOutlet weak var employeeSurnameField: UITextField!
   @IBOutlet weak var employeeIDField: UITextField!
   @IBOutlet weak var employeeBirthDateTxtFieldOutlet: UITextField!
}

In ViewController (the tag strategy shown below is being used for capturing the field edited through textFieldShouldReturn):

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "employeeCell") as! employeesTableViewCell!

    createPickerView(sender:  (cell?.employeeBirthDateTxtFieldOutlet)!)
    createToolbar(sender:  (cell?.employeeBirthDateTxtFieldOutlet)!)

    cell?.employeeNameField.tag = indexPath.row * 10 + 1
    cell?.employeeSurnameField.tag = indexPath.row * 10 + 2
    cell?.employeeIDDocField.tag = indexPath.row * 10 + 3
    cell?.employeeBirthDateTxtFieldOutlet.tag = indexPath.row * 10 + 5
    cell?.employeeNumberField.text = "Employee \(indexPath.row + 1)"

    return cell!
}

func createPickerView(sender: UITextField){
    let datePickerView : UIDatePicker = UIDatePicker()

    datePickerView.datePickerMode = UIDatePickerMode.date
    sender.inputView = datePickerView
    datePickerView.tag = sender.tag
    datePickerView.addTarget(self, action: #selector(datePickerValueChanged(caller:destination:)), for: UIControlEvents.valueChanged)
}

@objc func datePickerValueChanged(caller: UIDatePicker, destination: UITextField){
    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = DateFormatter.Style.medium
    dateFormatter.timeStyle = DateFormatter.Style.none

    destination.text = dateFormatter.string(from: caller.date)  <-- I expected I could update the text here, as I'm referencing it through the parameter, but it doesn't work. However, no error is raised.
}



func createToolbar(sender: UITextField){
    let datePickerToolbar = UIToolbar()
    datePickerToolbar.sizeToFit()

    let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(employeeRegisterController.dismissKeyboard))
    doneButton.tag = sender.tag

    datePickerToolbar.setItems([doneButton], animated: false)
    datePickerToolbar.isUserInteractionEnabled = true

    sender.inputAccessoryView = datePickerToolbar
}

@objc func dismissKeyboard(on: UIButton){
    view.endEditing(true)
}
General Grievance
  • 4,555
  • 31
  • 31
  • 45
Markussen
  • 164
  • 1
  • 13

1 Answers1

3

You can't pass more than one parameter in #selector() but you can pass the position by using an additional parameter

Then change the below two functions

func createPickerView(sender: UITextField){
    let datePickerView : UIDatePicker = UIDatePicker()

    datePickerView.datePickerMode = UIDatePickerMode.date
    sender.inputView = datePickerView
    datePickerView.tag = sender.tag
    datePickerView.addTarget(self, action: #selector(datePickerValueChanged(caller:)), for: UIControlEvents.valueChanged)
}

 func datePickerValueChanged(caller: UIDatePicker){
    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = DateFormatter.Style.medium
    dateFormatter.timeStyle = DateFormatter.Style.none

    let indexRow = (caller.tag - 5) / 10

    let indexPath = IndexPath(row: indexRow, section: 0)
    let cell = tableView.cellForRow(at: indexPath) as! employeesTableViewCell

    cell.employeeBirthDateTxtFieldOutlet.text = dateFormatter.string(from: caller.date)  
}

Hope this will help you

Ganesh Manickam
  • 2,113
  • 3
  • 20
  • 28
  • Thanks a lot!! I’ll test it today and I’ll let you know the result. I’ve a question about indexPath: I had thought about going that way but in other threads in this forum, some people say it’s not a good practice to use it out of tableView functions. Did I misunderstand? – Markussen Jan 21 '18 at 13:35
  • Just tested it and it works like a charm! Thanks! Regarding the line `let indexRow = (caller.tag - 5) / 10`, in case it helps, in order to grab the row and field in each row based on the field tag, I use the function `let (q,r) = caller.tag.quotientAndRemainder(dividingBy: 10)`, which returns quotient and remainder (quotient = row; remainder = field tag in row). For this to work, you need to assign a tag to the fields in the way `field.tag = row*10+field number` (if you're multiplying row by another number, the _dividingBy_ argument has to be changed to the same number). – Markussen Jan 21 '18 at 14:59
  • we need to get the index position of cell to access that cell contents. and we are passing index position via tag. But, you are passing some other values by multiply by 10 and added by 5 For Example: If index position is 1 but after adding and multiply you pass 15 into tag .at this time if we access same tag as row of index means it will get the access of 15th row of tableView For that reason i revert back and get original row to access particular cell which we selected – Ganesh Manickam Jan 22 '18 at 05:47