iosswiftuitableviewuidatepicker

UIDatePicker in UITextField in UITableView with custom UITableViewCell: realtime update


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)
}

Solution

  • 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