I'm trying to let the user pick a date and let it display in a text field. I've found so many answers online but none of them seem to work. I don't if they are outdated. I'm getting an error on "datePickerView.addTarget" in the selector part. When I run the app, I tap on the text field and the keyboard appears which is what I want. But the app crashes as soon as I change the date.
Any help would be really appreciated!
#IBAction func dateFieldEditing(sender: UITextField) {
let datePickerView:UIDatePicker = UIDatePicker()
datePickerView.datePickerMode = UIDatePickerMode.Date
sender.inputView = datePickerView
datePickerView.addTarget(self, action:Selector("handleDatePicker"),forControlEvents: UIControlEvents.ValueChanged)
}
func handleDatePicker (sender: UIDatePicker){
let formatter = NSDateFormatter()
formatter.dateStyle = .LongStyle
dateField.text = formatter.stringFromDate(sender.date)
}
The following works, assuming that you have a UITextField and UIDatePicker bound as IBOutlets via Interface Builder. Setting sender.inputView was unnecessary.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var datePicker: UIDatePicker?
#IBOutlet weak var textField: UITextField?
func handleDatePicker (sender: UIDatePicker) {
let formatter = NSDateFormatter()
formatter.dateStyle = .LongStyle
textField!.text = formatter.stringFromDate(sender.date)
}
override func viewDidLoad() {
super.viewDidLoad()
self.datePicker!.datePickerMode = UIDatePickerMode.Date
datePicker!.addTarget(self, action:#selector(handleDatePicker(_:)), forControlEvents: UIControlEvents.ValueChanged)
}
}
Related
First let me state all the other code you see works as desired except for the UIDatePicker. Right now when I click on the dateTextField the UIDatePicker shows up and I can select a date but when I click the Done button the Date selected is not in the UITextField assigned to it.
One area that may be a place to focus is the doneClicked function. When I initially created it I did not have the (#objc) in front of it and it gave me an error.
/Argument of '#selector' refers to instance method doneClicked() that is not exposed to Objective-C/
So I added (#objc) and the error went away but as stated above the date selected from the UIDatePicker is not being saved in the UITextField.
If you would please explain and show where my code is wrong and how to fix it.
Any help is greatly appreciated in this matter thank you very much.
import Foundation
import UIKit
import Firebase
import FirebaseUI
class TrainViewController: UIViewController{
#IBOutlet weak var trainField: UITextField!
#IBOutlet weak var locationField: UITextField!
#IBOutlet weak var engineField: UITextField!
#IBOutlet weak var dateTextField: UITextField!
let datePicker = UIDatePicker()
#IBAction func saveButton(_ sender: UIButton) {
self.saveText()
trainField.text?.removeAll()
locationField.text?.removeAll()
engineField.text?.removeAll()
dateTextField.text?.removeAll()
self.view.endEditing(true)
}
override func viewDidLoad() {
super.viewDidLoad()
createDatePicker()
}
func saveText() {
let trainText = self.trainField.text!
let locationText = self.locationField.text!
let engineText = self.engineField.text!
let dateStart = self.dateTextField.text!
let db = Firestore.firestore()
let dict = ["train_text": trainText,
"location_text": locationText,
"engine_text": engineText,
"dateStart_text": dateStart]
db.collection("users").addDocument(data: dict)
}
func createDatePicker(){
//format the display of our datepicker
datePicker.datePickerMode = .date
//assign date picker to our textfield
dateTextField.inputView = datePicker
//create a toolbar
let toolbar = UIToolbar()
toolbar.sizeToFit()
//add a done button on this toolbar
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(doneClicked))
toolbar.setItems([doneButton], animated: true)
dateTextField.inputAccessoryView = toolbar
}
#objc func doneClicked(){
//format the date display in textfield
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
}
}
extension TrainViewController : UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
you are not assigning the date in textFiled.
Like : dateTextField.text = dateString
in doneClicked click, get the date form datePicker and assign to dateTextField.
Code:
#objc func doneClicked(){
//format the date display in textfield
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
let dateString = dateFormatter.stringFromDate(datePicker.date)
print(dateString)
dateTextField.text = dateString
}
I just begin to learn iOS dev, here I want to show a datePicker and get date from the user, but I don't know how to show a DataPicker from the bottom of screen. After searching, I found that: dataPicker can be activated by a textField simply by "myTextField.inputView = myDatepicker". I did that, but I faced questions:
After activating the datePicker, the user is able to cut, paste the date shown in the textField. Is there some way to disable the selection of the content in the textField and also the editing menu?
Instead of using a textField, if I want to use a UIButton/UILabel/cell to activate the dataPicker, How can I do that?(I mean how to show the DatePicker from the bottom. I found UIButton/UILabel/cell have no method .inputView like textField does)
It may simple, but really confused me, this is the first app I am trying to do. Any help is very much appreciated, especially in detail, in Swift. Thank you very much.
Way 1
Use a button to show the date and a button to show/ hide the datepicker embedded in a view.
class ViewController: UIViewController {
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var datePicker: UIDatePicker!
#IBOutlet weak var chooseDate: UIButton!
#IBAction func toggleDatePicker(_ sender: Any) {
datePicker.isHidden = !datePicker.isHidden
}
override func viewDidLoad() {
dateLabel.text = stringFrom(date: Date())
datePicker.isHidden = true
}
}
extension ViewController: UIPickerViewDelegate {
#IBAction func valueChanged() {
dateLabel.text = stringFrom(date: datePicker.date)
}
private func stringFrom(date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
return dateFormatter.string(from: datePicker.date)
}
}
Way 2
Use a button to show the date and a button to show/ hide the datepicker from the bottom.
Just use the following protocol that I wrote:
protocol DatePickable: class {
var textField: UITextField { get }
var datePicker: UIDatePicker { get }
var isDatePickerVisible: Bool { get set }
}
extension DatePickable {
func prepareDatePickerFor(view: UIView, onChangeAction: Selector) {
datePicker.datePickerMode = UIDatePickerMode.date
datePicker.addTarget(self, action: onChangeAction, for: .valueChanged)
textField.inputView = datePicker
view.addSubview(textField)
}
func toggleDatePicker() {
isDatePickerVisible ? hideDatePicker() : showDatePicker()
isDatePickerVisible = !isDatePickerVisible
}
private func showDatePicker() {
textField.becomeFirstResponder()
}
private func hideDatePicker() {
textField.resignFirstResponder()
}
}
How to use the protocol in you class:
class ViewController: UIViewController, DatePickable {
// Required by Datepickable
var isDatePickerVisible = false
var textField: UITextField = UITextField()
var datePicker: UIDatePicker = UIDatePicker()
// IBOutlets
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var chooseDate: UIButton!
override func viewDidLoad() {
prepareDatePickerFor(view: view,
onChangeAction: #selector(ViewController.onDidChangeDate(_:)))
dateLabel.text = stringFrom(date: Date())
}
#IBAction func toggleDatePickerVisibility(_ sender: Any) {
toggleDatePicker()
}
#IBAction func onDidChangeDate(_ sender: Any) {
dateLabel.text = stringFrom(date: datePicker.date)
}
private func stringFrom(date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
return dateFormatter.string(from: datePicker.date)
}
}
So you have to add textfield delegate and then recognise the right textfield:
let datePicker = UIDatePicker()
func viewDidLoad() {
super.viewDidLoad()
txtDatePicker.delegate = self
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField == txtDatePicker {
showDatePicker()
}
return true
}
Then inside showDatePicker you will tell your picker what mode should it be as follows:
func showDatePicker() {
//Formate Date
datePicker.datePickerMode = .date
// if you need a toolbar, here is a good place to define it
// add datepicker to textField
txtDatePicker.inputView = datePicker
}
Then in delegate function or toolbar selection you do as follows:
func donedatePicker() {
//For date formate
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy"
txtDatePicker.text = formatter.string(from: datePicker.date)
self.view.endEditing(true)
}
I'm trying to update two buttons with datepicker. Button startDate et endDate. I don't how can I update these butttons with differents datepicker.
Maybe with handler ?
user story : the user click on startDat datepicker is displaying, user choose any date or time, then click on endDate and same action.
EDIT:
class ViewController: UIViewController {
#IBOutlet weak var dateTextField: UITextField!
#IBOutlet weak var dateTextLabel: UILabel!
var whoTriggeredPickerView: UIButton?
#IBOutlet weak var startDateTextButton : UIButton!
#IBOutlet weak var endDateTextButton : UIButton!
#IBOutlet weak var datePick : UIDatePicker!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func donePressed(_ sender: UIBarButtonItem) {
dateTextField.resignFirstResponder()
dateTextLabel.resignFirstResponder()
startDateTextButton.resignFirstResponder()
endDateTextButton.resignFirstResponder()
}
func donePressedButton(_ sender :UIButton){
startDateTextButton.resignFirstResponder()
endDateTextButton.resignFirstResponder()
}
func tappedToolBarBtn(_ sender: UIBarButtonItem) {
let dateformatter = DateFormatter()
startDateTextButton.setTitle(dateformatter.string(from: Date()), for: .normal)
endDateTextButton.setTitle(dateformatter.string(from: Date()), for: .normal)
startDateTextButton.resignFirstResponder()
endDateTextButton.resignFirstResponder()
}
func closeDatePicker(){
self.view.endEditing(true)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
closeDatePicker()
}
#IBAction func BtnClicked(sender: UIButton) {
let datePickerView: UIDatePicker = UIDatePicker()
datePickerView.datePickerMode = UIDatePickerMode.date
//sender.inputView = datePickerView
self.view.addSubview(datePickerView)
datePickerView.addTarget(self, action: #selector(ViewController.datePickerValueChanged), for: UIControlEvents.valueChanged)
self.whoTriggeredPickerView = sender
}
func datePickerValueChanged(_ sender: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = DateFormatter.Style.medium
dateFormatter.timeStyle = DateFormatter.Style.none
//dateTextField.text = dateFormatter.string(from: sender.date)
if self.whoTriggeredPickerView == startDateTextButton {
// set startDateBtn title
startDateTextButton.setTitle(dateFormatter.string(from: Date()), for: .normal)
}else if self.whoTriggeredPickerView == endDateTextButton {
// set endDateBtn title
endDateTextButton.setTitle(dateFormatter.string(from: Date()), for: .normal)
}
}
}
For your question:
You can create a tmp variable to record picker is triggered by which button. Then you can decide which button's title you want to set.
//In your view controller:
var whoTriggeredPickerView: UIButton?
//In your BtnClicked function:
self.whoTriggeredPickerView = sender
//Then in datePickerValueChanged function:
if self.whoTriggeredPickerView == StartDateButton {
// set startDateBtn title
}else if self.whoTriggerPickerView == endDateButton {
// set endDateBtn title
}
Some suggestions:
always lowercase the instance name, you are using DatePick as the instance name of UIDatePicker, instead, you should name it as datePicker; for function names as well.
for your case you can use two textfields instead of buttons.
let datePicker = UIDatePicker()
datePicker.addTarget(self, action: #selector(dateChanged), for: .valueChanged)
startDateTextField.inputView = datePicler
endDateTextField.inputView = datePicker
I'm making a To-Do app and I want the value of the date picker as saved by the user into a variable so that I can print that variable in a tableView.
This is my datePicker code:
#IBOutlet weak var datePicker: UIDatePicker!
override func viewDidLoad() {
super.viewDidLoad()
datePicker.minimumDate = Date()
datePicker.locale = Locale.current
datePicker.setValue(UIColor.gray, forKeyPath: "textColor")
}
You can add a specific target to you Picker in order to get a new value.
For example :
#IBOutlet weak var datePicker: UIDatePicker!
var selectedDate : Date?
override func viewDidLoad() {
super.viewDidLoad()
self.datePicker.addTarget(self, action: #selector(self.storeSelectedRow), for: UIControlEvents.valueChanged)
}
func storeSelectedRow(){
self.selectedDate = self.datePicker.date
}
OR
With the storyboard:
As you add you pickerView as a #IBOutlet you can add a #IBAction to your view controller and then get the date of your picker and store it to a variable.
Please see this example that pass the Picker Value to another ViewController
https://drive.google.com/file/d/0B2RAeG6eqtvCbWp1VE5IZ0E2Vkk/view?usp=sharing
Add follow code in viewDidLoad.
datePicker.addTarget(self, action: "dateChanged:",
forControlEvents: UIControlEvents.ValueChanged)
Then get value in func dateChanged.
func dateChanged(datePicker : UIDatePicker){
let formatter = NSDateFormatter()
//formatter
formatter.dateFormat = "yyyy年MM月dd日 HH:mm:ss"
print(formatter.stringFromDate(datePicker.date))
//there you get your label first
//let label = ...
label.text = formatter.stringFromDate(datePicker.date)
}
I have a DatePicker where the user selects time and date fields, the keyboard shows up first and then I have to click the textfield again for the Picker to appear. What I want is for the DatePicker to show up on the first try.
here is my code
#IBOutlet var dateField: UITextField! = UITextField()
#IBOutlet var timeField: UITextField! = UITextField()
#IBAction func userPickDate(sender: UITextField) {
var datePickerView : UIDatePicker = UIDatePicker()
datePickerView.datePickerMode = UIDatePickerMode.Date
sender.inputView = datePickerView
datePickerView.addTarget(self, action: Selector("handleDatePicker:"), forControlEvents: UIControlEvents.ValueChanged)
}
#IBAction func userPickTime(sender: UITextField) {
var datePickerView : UIDatePicker = UIDatePicker()
datePickerView.datePickerMode = UIDatePickerMode.Time
sender.inputView = datePickerView
datePickerView.addTarget(self, action: Selector("handleTimePicker:"), forControlEvents: UIControlEvents.ValueChanged)
}
func handleTimePicker(sender: UIDatePicker)
{
var timeFormatter = NSDateFormatter()
timeFormatter.timeStyle = .ShortStyle
timeField.text = timeFormatter.stringFromDate(sender.date)
}
func handleDatePicker(sender: UIDatePicker) {
var timeFormatter = NSDateFormatter()
timeFormatter.dateStyle = .MediumStyle
//timeFormatter.timeStyle = .ShortStyle
dateField.text = timeFormatter.stringFromDate(sender.date)
}
Discard the last answer. I understood whAT you are doing. Here is what I found: Placing the declaration of the UPickerView objects in the scope of the class rather than localized to the handler seemed to work. Here is the code. Note the addition of the resignFirstResponder().
import UIKit
class ViewController: UIViewController {
var datePickerView : UIDatePicker = UIDatePicker()
var timePickerView : UIDatePicker = UIDatePicker()
#IBOutlet weak var dateField: UITextField!
#IBOutlet weak var timeField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
datePickerView.datePickerMode = UIDatePickerMode.Date
dateField.inputView = datePickerView
datePickerView.addTarget(self, action: Selector("handleDatePicker:"), forControlEvents: UIControlEvents.ValueChanged)
handleDatePicker(datePickerView)
timePickerView.datePickerMode = UIDatePickerMode.Time
timeField.inputView = timePickerView
timePickerView.addTarget(self, action: Selector("handleTimePicker:"), forControlEvents: UIControlEvents.ValueChanged)
handleTimePicker(timePickerView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func handleDatePicker(sender: UIDatePicker) {
var dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = .MediumStyle
//timeFormatter.timeStyle = .ShortStyle
dateField.text = dateFormatter.stringFromDate(sender.date)
dateField.resignFirstResponder()
}
func handleTimePicker(sender: UIDatePicker) {
var timeFormatter = NSDateFormatter()
timeFormatter.timeStyle = .ShortStyle
timeField.text = timeFormatter.stringFromDate(sender.date)
timeField.resignFirstResponder()
}
}
You need to place an action on your textField as such:
#IBAction func userDOBSelectedAction(sender: UITextField) {
userDOBTextField.resignFirstResponder()
}
This is an updated version of Syed's answer for Swift 2.2 & includes some formatting updates.
import UIKit
class ViewController: UIViewController {
var datePickerView : UIDatePicker = UIDatePicker()
var timePickerView : UIDatePicker = UIDatePicker()
#IBOutlet weak var dateField: UITextField!
#IBOutlet weak var timeField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
datePickerView.datePickerMode = .Date
dateField.inputView = datePickerView
datePickerView.addTarget(self, action: #selector(ViewController.handleDatePicker(_:)), forControlEvents: .ValueChanged)
handleDatePicker(datePickerView)
timePickerView.datePickerMode = .Time
timeField.inputView = timePickerView
timePickerView.addTarget(self, action: #selector(ViewController.handleTimePicker(_:)), forControlEvents: .ValueChanged)
handleTimePicker(timePickerView)
}
func handleDatePicker(sender: UIDatePicker) {
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = .MediumStyle
dateField.text = dateFormatter.stringFromDate(sender.date)
dateField.resignFirstResponder()
}
func handleTimePicker(sender: UIDatePicker) {
let timeFormatter = NSDateFormatter()
timeFormatter.timeStyle = .ShortStyle
timeField.text = timeFormatter.stringFromDate(sender.date)
timeField.resignFirstResponder()
}
}