'DatePicker not closing after tap outside

In my application I have a textfield to add the date of birth. I add a datePicker to popup once user tap on the textfield. datePicker display correctly and after user select the date it is displaying in the textfield. The problem is after select the date the datePicker is not closing.

This is my code:

class BController: UIViewController, UITextFieldDelegate {

 @IBOutlet weak var dateOfBirth: UITextField!

    let datePicker = UIDatePicker()




    func textFieldShouldReturn(textField: UITextField) -> Bool {
         textField.resignFirstResponder()
       // self.view.endEditing(true)
        return true
    }



    func textFieldDidBeginEditing(textField: UITextField){
         if textField == dateOfBirth{
           // let datePicker = UIDatePicker()

            datePicker.datePickerMode = UIDatePickerMode.Date

            datePicker.backgroundColor = UIColor.blackColor()
            datePicker.setValue(UIColor.whiteColor(), forKey: "textColor")

            textField.inputView = datePicker
            datePicker.addTarget(self, action: "datePickerChanged:", forControlEvents: .ValueChanged)
        }
    }

    func datePickerChanged(sender: UIDatePicker){

        let formatter = NSDateFormatter()
        formatter.dateFormat = "dd-MM-yyyy"
        //    formatter.dateStyle = .ShortStyle
        //  formatter.timeStyle = .NoStyle
        dateOfBirth.text = formatter.stringFromDate(sender.date)

    }

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        print("touch happened")
        self.view.endEditing(true)

    }


    override func viewDidLoad() {
        super.viewDidLoad()
        dateOfBirth.delegate = self
        text1.delegate =  self
        text2.delegate = self

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }



}

I got console output as:

unrecognized selector sent to instance 0x7 Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BController getCalendar:]: unrecognized selector sent to instance 0x78789050'

How to solve this issue? I'm using Xcode 6.2. If you need more information please let me know.



Solution 1:[1]

As documentation say:

var inputView: UIView? The custom input view to display when the text field becomes the first responder.

So you should first initialize your datePicker, and then assign it to dateOfBirth's inputView property in viewDidLoad method instead textFieldDidBeginEditing method.

For closing datePicker on tap outside use UITapGestureRecognizer added on self.view

class ViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let datePicker = UIDatePicker.init()
        textField.inputView = datePicker

        let gestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(backgroundTap(gesture:)));
        view.addGestureRecognizer(gestureRecognizer)

    }

    func backgroundTap(gesture : UITapGestureRecognizer) {
        textField.resignFirstResponder() // or view.endEditing(true)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

Your mistake is that when textFieldDidBeginEditing method call, UITextField knows nothing about UIDatePicker.

Solution 2:[2]

I know this is an old question, and a slightly different approach to showing a date picker but I think the idea can still be applied.

Try wrapping the Date Picker View in a UIView() container which contains the gesture detector and spans the full size of the screen.

class ViewController: UIViewController {

var toolBar = UIToolbar()
var datePicker  = UIDatePicker()
var datePickerContainer = UIView()

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

@IBAction func showDatePicker(_ sender: UIButton) {
    datePicker = UIDatePicker()
    datePicker.autoresizingMask = .flexibleWidth
    datePicker.datePickerMode = .dateAndTime
    datePicker.preferredDatePickerStyle = .wheels
    datePicker.backgroundColor = .clear
    
    datePicker.addTarget(self, action: #selector(self.dateChanged(_:)), for: .valueChanged)
    datePicker.frame = CGRect(x: 0.0, y: UIScreen.main.bounds.size.height - 300, width: UIScreen.main.bounds.size.width, height: 300)
    
    datePickerContainer = UIView()
    datePickerContainer.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)
    datePickerContainer.backgroundColor = .clear
    datePickerContainer.addSubview(datePicker)
    self.view.addSubview(datePickerContainer)
    
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(touchedOutsideOverlay(_:)))
    datePickerContainer.addGestureRecognizer(tapGesture)
    
    toolBar = UIToolbar(frame: CGRect(x: 0, y: UIScreen.main.bounds.size.height - 300, width: UIScreen.main.bounds.size.width, height: 50))
    toolBar.barStyle = .black
    toolBar.items = [
        UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
        UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.onDoneButtonClick))
    ]
    toolBar.sizeToFit()
    self.view.addSubview(toolBar)
}

@objc func dateChanged(_ sender: UIDatePicker?) {
    let dateFormatter = DateFormatter()
    
    if let date = sender?.date {
        print("Picked the date: \(dateFormatter.string(from: date))")
    }
}

@objc func touchedOutsideOverlay(_ sender: UITapGestureRecognizer){
    removeDatePicker()
}

@objc func onDoneButtonClick() {
    removeDatePicker()
}

func removeDatePicker(){
    toolBar.removeFromSuperview()
    datePicker.removeFromSuperview()
    datePickerContainer.removeFromSuperview()
}
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Paul Mayer