DatePicker opening on second click on TextField - ios

I have a TextField on click of which a custom date picker should open. But on first click keyboard opens and on second click the date picker opens. Please help me how to show date picker on first click. eodDateTime my TextField here where i want date picker to show on click of textfield and show the selected date time in the same textfield
Here is my code:
import UIKit
class ATMDiscrepancyViewController: UIViewController, UIScrollViewDelegate,UITextFieldDelegate
{
var datePicker : UIDatePicker!
#IBOutlet weak var remarks: UITextField!
#IBOutlet weak var lastTransaction: UITextField!
#IBOutlet weak var eodDateTime: UITextField!
var atmData : SelectedATM!
#IBOutlet weak var scrollView: UIScrollView!
var activeField: UITextField?
var selectedDate : String = ""
var alertControllerScanCode:CustViewController!
let button = UIButton(type: UIButtonType.custom)
}
#IBOutlet weak var saveAction: UIButton!
func datePickerValuechanged (sender : UIDatePicker)
{
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = DateFormatter.Style.medium
dateFormatter.timeStyle = DateFormatter.Style.none
eodDateTime.text = dateFormatter.string(from: sender.date)
selectedDate = dateFormatter.string(from: sender.date)
self.view.endEditing(true)
}
#IBAction func eodAction(_ sender: UITextField) {
deregisterFromKeyboardNotifications()
self.button.isHidden=true
self.pickUpDate(eodDateTime)
}
func pickUpDate(_ textField : UITextField){
deregisterFromKeyboardNotifications()
self.button.isHidden=true
self.button.isEnabled = false
NotificationCenter.default.addObserver(self, selector: #selector(ATMDiscrepancyViewController.keyboardRemoveReturn(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
// DatePicker
self.datePicker = UIDatePicker(frame:CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 216))
self.datePicker.backgroundColor = UIColor.white
self.datePicker.datePickerMode = UIDatePickerMode.dateAndTime
textField.inputView = self.datePicker
// ToolBar
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.isTranslucent = true
toolBar.tintColor = UIColor(red: 92/255, green: 216/255, blue: 255/255, alpha: 1)
toolBar.sizeToFit()
// Adding Button ToolBar
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(ATMDiscrepancyViewController.doneClick))
let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(ATMDiscrepancyViewController.cancelClick))
toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
textField.inputAccessoryView = toolBar
}
func doneClick() {
let dateFormatter1 = DateFormatter()
// let timeFormatter = DateFormatter()
dateFormatter1.dateStyle = .medium
dateFormatter1.timeStyle = .none
dateFormatter1.dateFormat = "yyyy-MM-dd HH:mm:SS"
eodDateTime.text = dateFormatter1.string(from: datePicker.date)
eodDateTime.resignFirstResponder()
}
func cancelClick() {
eodDateTime.resignFirstResponder()
}
func handleDatePicker(sender: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
eodDateTime.text = dateFormatter.string(from: sender.date)
selectedDate = dateFormatter.string(from: sender.date)
}
func doneButton(sender:UIButton)
{
eodDateTime.resignFirstResponder() // To resign the inputView on clicking done.
eodDateTime.text = selectedDate
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Discrepancy"
registerForKeyboardNotifications()
button.setTitle("Return", for: UIControlState())
button.setTitleColor(UIColor.black, for: UIControlState())
button.frame = CGRect(x: 0, y: 163, width: 106, height: 53)
button.adjustsImageWhenHighlighted = false
button.addTarget(self, action: #selector(ATMDiscrepancyViewController.Done(_:)), for: UIControlEvents.touchUpInside)
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ATMDiscrepancyViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
self.remarks.delegate = self
self.lastTransaction.delegate = self
self.eodDateTime.delegate = self
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == self.lastTransaction || textField == self.remarks {
let textString = (textField.text as! NSString).replacingCharacters(in: range, with: string)
return true
}
let allowedCharacters = CharacterSet.decimalDigits
let characterSet = CharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.lastTransaction.resignFirstResponder()
self.remarks.resignFirstResponder()
self.button.isHidden=true
self.lastTransaction.becomeFirstResponder()
dismissKeyboard()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
if textField == self.lastTransaction
{
if isEmptyValidate(edit: self.lastTransaction) == true
{
return false
}
self.button.isHidden = true
self.remarks.becomeFirstResponder()
}
if textField == self.remarks
{
if isEmptyValidate(edit: self.remarks)
{
return false
}
}
return true
}
func valid() -> Bool
{
if isEmptyValidate(edit: eodDateTime) == true
{
return false
}
return true
}
func isEmptyValidate(edit : UITextField) ->Bool
{
if edit.text?.isEmpty ?? true
{
edit.attributedPlaceholder = NSAttributedString(string: "Enter value", attributes: [NSForegroundColorAttributeName : UIColor.red])
return true
}
return false
}
func registerForKeyboardNotifications(){
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func deregisterFromKeyboardNotifications(){
//Removing notifies on keyboard appearing
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWasShown(notification: NSNotification){
//Need to calculate keyboard exact size due to Apple suggestions
self.scrollView.isScrollEnabled = true
var info = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeField = self.activeField{
if (!aRect.contains(activeField.frame.origin)){
self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}
func keyboardWillBeHidden(notification: NSNotification){
//Once keyboard disappears, restore original positions
var info = notification.userInfo!
self.view.endEditing(true)
self.scrollView.isScrollEnabled = true
}
func textFieldDidBeginEditing(_ textField: UITextField){
if textField == self.lastTransaction {
//self.button.isHidden = true;
NotificationCenter.default.addObserver(self, selector: #selector(ATMDiscrepancyViewController.keyboardRemoveReturn(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
}
activeField = textField
NotificationCenter.default.addObserver(self, selector: #selector(ATMDiscrepancyViewController.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
}
func textFieldDidEndEditing(_ textField: UITextField){
activeField = nil
}
func dismissKeyboard() {
view.endEditing(true)
}
func keyboardWillShow(_ note : Notification) -> Void{
DispatchQueue.main.async { () -> Void in
self.button.isHidden = false
self.scrollView.isScrollEnabled = true
var info = note.userInfo!
let keyBoardWindow = UIApplication.shared.windows.last
self.button.frame = CGRect(x: 0, y: (keyBoardWindow?.frame.size.height)!-53, width: 106, height: 53)
keyBoardWindow?.addSubview(self.button)
keyBoardWindow?.bringSubview(toFront: self.button)
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeField = self.activeField {
if (!aRect.contains(activeField.frame.origin)){
self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
}
}
UIView.animate(withDuration: (((note.userInfo! as NSDictionary).object(forKey: UIKeyboardAnimationCurveUserInfoKey) as AnyObject).doubleValue)!, delay: 0, options: UIViewAnimationOptions.curveEaseIn, animations: { () -> Void in
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: 0)
}, completion: { (complete) -> Void in
})
}
}
func Done(_ sender : UIButton){
DispatchQueue.main.async { () -> Void in
self.textFieldShouldReturn(self.activeField!)
}
}
func keyboardRemoveReturn(_ note : Notification) -> Void{
DispatchQueue.main.async { () -> Void in
self.button.isHidden = true
UIView.animate(withDuration: (((note.userInfo! as NSDictionary).object(forKey: UIKeyboardAnimationCurveUserInfoKey) as AnyObject).doubleValue)!, delay: 0, options: UIViewAnimationOptions.curveEaseIn, animations: { () -> Void in
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: 0)
}, completion: { (complete) -> Void in
})
}
}
}
Thanks

Make first responder on button click
#IBAction func eodAction(_ sender: UITextField) {
deregisterFromKeyboardNotifications()
self.button.isHidden=true
eodDateTime.becomeFirstResponder()
// self.pickUpDate(eodDateTime)
}
And implement textFieldShouldBeginEditing method to show date picket in input view
extension ATMDiscrepancyViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField == eodDateTime {
self.pickUpDate(eodDateTime)
}
}
}

I could not understand the whole code but the best way to fix this would be to assign the picker as inputView in viewDidLoad. So place the following code, you are using, in viewDidLoad and it should work fine :
self.datePicker = UIDatePicker(frame:CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 216))
self.datePicker.backgroundColor = UIColor.white
self.datePicker.datePickerMode = UIDatePickerMode.dateAndTime
textField.inputView = self.datePicker

Related

How to implement the 'datepicker' correctly using swift when adding the multiple entries to server?

**Aim: ** To show latest apple calendar type of date picker(inline) in app when user wants to select the date.How it should look
Scenario:**
The user can input multiple entries (maximum 3) the exercise(suryanamaskar). These multiple entries has two functions
1.date-picker and
2.number entry(number of 'suryanamaskar')
When user clicks on the date-picker button. the date picker shows up (.inline). Each entry fetches the post API call that saves the 'suryanamaskar' count entry on server.
What is the issue: I tried to implement the date-picker using and few UI frames : .inline
I replaced the date-picker completely and implemented ".wheels" that works fine.
The datepicker is show up correctly first time. But when the user tried to add other entry or press the date entry buttton . The datepicker gets cut and is not properly visible. This is how it looks when user clicks on datepicker button second time.
What my code looks like:
import UIKit
import SwiftyJSON
import Alamofire
struct surynamaskarStruct {
var date: String
var count: String
}
class AddSuryaNamskarCountVC: UIViewController, UITextFieldDelegate {
var surynamaskarData: [surynamaskarStruct] = [
surynamaskarStruct(date: "Date", count: "0"),
surynamaskarStruct(date: "Date", count: "0"),
surynamaskarStruct(date: "Date", count: "0")
]
#IBOutlet var tableView: UITableView!
#IBOutlet weak var lblMember: UILabel!
#IBOutlet weak var btnMember: UIButton!
#IBOutlet weak var familyMemberHeightConstraint: NSLayoutConstraint!
var viewPicker : UIView!
let datePicker = UIDatePicker()
var btnIndex : Int!
var strMemberId : String!
var dicMember = [[String:Any]]()
lazy var loader : UIActivityIndicatorView = {
let indicator = UIActivityIndicatorView(style: .large)
indicator.hidesWhenStopped = true
return indicator
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if self.dicMember.count > 0 {
self.strMemberId = self.dicMember[0]["id"] as? String
self.lblMember.text = self.dicMember[0]["name"] as? String
self.familyMemberHeightConstraint.constant = 90
} else {
self.familyMemberHeightConstraint.constant = 0
}
view.addSubview(loader)
loader.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
loader.centerXAnchor.constraint(equalTo: view.centerXAnchor),
loader.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
override func viewWillAppear(_ animated: Bool) {
navigationBarDesign(txt_title: "Record Suryanamskar", showbtn: "back")
}
#IBAction func onMemberClick(_ sender: UIButton) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "PickerTableViewWithSearchViewController") as! PickerTableViewWithSearchViewController
vc.selectedItemCompletion = {dict in
self.lblMember.text = dict["name"] as? String
self.strMemberId = dict["id"] as? String
}
vc.dataSource = dicMember
vc.strNavigationTitle = "Family Member"
vc.modalPresentationStyle = UIModalPresentationStyle.fullScreen
self.present(vc, animated: true, completion: nil)
}
#IBAction func onCancelClick(_ sender: UIButton) {
self.dismiss(animated: true)
}
#IBAction func onSaveClick(_ sender: UIButton) {
var arrData = [[String:Any]]()
for arr in surynamaskarData {
if arr.date != "Date" && arr.count != "0" {
var dict = [String : Any]()
dict["date"] = arr.date
dict["count"] = arr.count
arrData.append(dict)
}
}
if arrData.count > 0 {
self.saveSuryaNamskarCountDataAPI(arrData)
////static controller to directly update the suryanamaskar chart instead of going back and then getting updated suryanamaskar
let alertController = UIAlertController(title:APP.title, message: "Suryanamaskar has been saved successfully!", preferredStyle:.alert)
let Action = UIAlertAction.init(title: "Ok", style: .default) { (UIAlertAction) in
// Write Your code Here
let vc = self.storyboard?.instantiateViewController(withIdentifier: "SuryaNamskarVC") as! SuryaNamskarVC
vc.modalPresentationStyle = UIModalPresentationStyle.fullScreen
// vc.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
self.present(vc, animated: true, completion: nil)
}
alertController.addAction(Action)
self.present(alertController, animated: true, completion: nil)
//// Till here for alert controller. if not implemented, updated suryanamaskar will happen only once user goes back and comes back to suryanamaskarVC
}
// else {
// showAlert(title: APP.title, message: "Please select date and count before Save")
// }
}
// MARK: - DatePicker functions
func showDatePicker(){
viewPicker = UIView(frame: CGRect(x: 0.0, y: self.view.frame.height - 380, width: self.view.frame.width, height: 380))
viewPicker.backgroundColor = UIColor.white
viewPicker.clipsToBounds = true
// Posiiton date picket within a view
datePicker.frame = CGRect(x: 10, y: 50, width: self.view.frame.width, height: 200)
datePicker.datePickerMode = .date
datePicker.minimumDate = Calendar.current.date(byAdding: .month, value: -2, to: Date())
datePicker.maximumDate = Calendar.current.date(byAdding: .year, value: 0, to: Date())
datePicker.backgroundColor = UIColor.white
if #available(iOS 14.0, *) {
datePicker.preferredDatePickerStyle = .inline
} else {
if #available(iOS 13.4, *) {
datePicker.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions
}
// Fallback on earlier versions
}
// Add an event to call onDidChangeDate function when value is changed.
datePicker.addTarget(self, action: #selector(AddMemberStep1VC.datePickerValueChanged(_:)), for: .valueChanged)
datePicker.center.x = self.view.center.x
//ToolBar
var toolbar = UIToolbar();
toolbar.sizeToFit()
toolbar = UIToolbar(frame: CGRect(x: 0.0, y: 0.0, width: self.view.frame.width, height: 50))
let doneButton = UIBarButtonItem(title: "done".localized, style: .plain, target: self, action: #selector(donedatePicker));
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "cancel".localized, style: .plain, target: self, action: #selector(cancelDatePicker));
toolbar.setItems([doneButton,spaceButton,cancelButton], animated: false)
// txtDate.inputAccessoryView = toolbar
// txtDate.inputView = datePicker
self.viewPicker.addSubview(toolbar)
self.viewPicker.addSubview(datePicker)
self.viewPicker.clipsToBounds = true
self.view.addSubview(self.viewPicker)
}
#objc func datePickerValueChanged(_ sender: UIDatePicker){
// Create date formatter
let dateFormatter: DateFormatter = DateFormatter()
// Set date format
dateFormatter.dateFormat = "dd/MM/yyyy"
// Apply date format
let _: String = dateFormatter.string(from: sender.date)
// print("Selected value \(selectedDate)")
}
#objc func donedatePicker(){
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy"
let strDate = formatter.string(from: datePicker.date)
surynamaskarData[btnIndex].date = strDate
DispatchQueue.main.async {
self.tableView.reloadData()
}
self.viewPicker.isHidden = true
}
#objc func cancelDatePicker(){
self.viewPicker.isHidden = true
}
#objc func onAddMoreClicked() {
print("AddMore Button Pressed")
for i in surynamaskarData {
if i.date == "Date" || i.count == "0" || i.count == "" {
showAlert(title: APP.title, message: "Please select date and count before AddMore")
return
}
}
surynamaskarData.append(surynamaskarStruct(date: "Date", count: "0"))
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
#objc func onDateClick(_ sender: UIButton) {
btnIndex = sender.tag
view.endEditing(true)
if self.viewPicker == nil {
self.showDatePicker()
} else {
if self.viewPicker.isHidden == true {
self.showDatePicker()
} else {
self.viewPicker.isHidden = true
}
}
}
#objc func onDeleteClick(_ sender: UIButton) {
surynamaskarData.remove(at: sender.tag)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if self.viewPicker != nil {
if self.viewPicker.isHidden == false {
self.viewPicker.isHidden = false
}
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if textField.text == "" {
textField.text = "0"
}
surynamaskarData[textField.tag].count = textField.text ?? "0"
}
func json(from object:Any) -> String? {
guard let data = try? JSONSerialization.data(withJSONObject: object, options: []) else {
return nil
}
return String(data: data, encoding: String.Encoding.utf8)
}
func saveSuryaNamskarCountDataAPI(_ data : [[String:Any]]) {
var parameters: [String: Any] = [:]
parameters["surynamaskar"] = self.json(from: data)
parameters["member_id"] = self.strMemberId // _appDelegator.dicDataProfile![0]["member_id"] as? String
print(parameters)
// APIUrl.save_suryanamaskar
//live API only
//"https://myhss.org.uk/api/v1/suryanamaskar/save_suryanamaskar_count"
loader.startAnimating()
let url = URL(string: APIUrl.save_suryanamaskar)!
AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default)
.validate()
.responseJSON { response in
self.loader.stopAnimating()
switch response.result {
case .success(let response):
print(response)
let jsonData = JSON(response)
if let status = jsonData["status"].int
{
if status == 1
{
let strMessage : String = jsonData["message"].rawValue as! String
print(strMessage)
// create the alert
let alert = UIAlertController(title: APP.title, message: strMessage, preferredStyle: UIAlertController.Style.alert)
// add an action (button)
let ok = UIAlertAction(title: "Ok".localized, style: .default, handler: { action in
DispatchQueue.main.async {
self.dismiss(animated: true)
}
})
alert.addAction(ok)
// show the alert
self.present(alert, animated: true, completion: nil)
} else {
if let strError = jsonData["message"].string {
showAlert(title: APP.title, message: strError)
}
}
}
case .failure(let error):
print(error.localizedDescription)
showAlert(title: APP.title, message: error.localizedDescription)
}
}
}
}
extension AddSuryaNamskarCountVC : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return surynamaskarData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: AddSuryaNamskarCountTVCell.cellIdentifier) as! AddSuryaNamskarCountTVCell
cell.btnDate.tag = indexPath.row
cell.btnDate.addTarget(self, action: #selector(self.onDateClick(_:)), for: .touchUpInside)
cell.btnCancel.tag = indexPath.row
cell.btnCancel.addTarget(self, action: #selector(self.onDeleteClick(_:)), for: .touchUpInside)
cell.lblDate.text = surynamaskarData[indexPath.row].date
cell.txtCount.text = surynamaskarData[indexPath.row].count
cell.txtCount.tag = indexPath.row
cell.txtCount.delegate = self
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60.0
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let viewFooter = UIView()
viewFooter.backgroundColor = UIColor.systemGray5
let size = tableView.frame.size
let addMoreButton = UIButton()
addMoreButton.setTitle("+ Add More", for: .normal)
addMoreButton.setTitleColor(Colors.txtAppDarkColor, for: .normal)
addMoreButton.frame = CGRect(x: 0, y: 0, width: size.width, height: 50)
addMoreButton.addTarget(self, action: #selector(onAddMoreClicked), for: .touchUpInside)
viewFooter.addSubview(addMoreButton)
return viewFooter
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 50
}
}
Changing the Co-ordinates works:
Following co-orinates fixes the calender view for this issue.
func showDatePicker(){
viewPicker = UIView(frame: CGRect(x: 0.0, y: self.view.frame.height - 500, width: self.view.frame.width, height: 500))
viewPicker.backgroundColor = UIColor.white
viewPicker.clipsToBounds = true
// Posiiton date picket within a view
datePicker.frame = CGRect(x: 10, y: 50, width: self.view.frame.width, height: 500)
datePicker.datePickerMode = .date
datePicker.minimumDate = Calendar.current.date(byAdding: .month, value: -2, to: Date())
datePicker.maximumDate = Calendar.current.date(byAdding: .year, value: 0, to: Date())
datePicker.backgroundColor = UIColor.white
if #available(iOS 14.0, *) {
datePicker.preferredDatePickerStyle = .inline
} else {
if #available(iOS 13.4, *) {
datePicker.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions
}
// Fallback on earlier versions
}
// Add an event to call onDidChangeDate function when value is changed.
datePicker.addTarget(self, action: #selector(AddMemberStep1VC.datePickerValueChanged(_:)), for: .valueChanged)
datePicker.center.x = self.view.center.x
//ToolBar
var toolbar = UIToolbar();
toolbar.sizeToFit()
toolbar = UIToolbar(frame: CGRect(x: 0.0, y: 0.0, width: self.view.frame.width, height: 50))
let doneButton = UIBarButtonItem(title: "done".localized, style: .plain, target: self, action: #selector(donedatePicker));
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "cancel".localized, style: .plain, target: self, action: #selector(cancelDatePicker));
toolbar.setItems([doneButton,spaceButton,cancelButton], animated: false)
self.viewPicker.addSubview(toolbar)
self.viewPicker.addSubview(datePicker)
self.viewPicker.clipsToBounds = true
self.view.addSubview(self.viewPicker)
}

Tapping on Date Picker does not popup Picker view in iOS13

In the app when I tap on the UITextField that should give me a UIDatePicker does not show me UIDatePicker on updating the iPhone to iOS 13. Works fine with iOS 12+ versions.
I am working on latest Xcode 11 and macOS Mojave 10.14.6
This is the code I am using:
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
let dateString: String!
activeTextField = textField
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
if activeTextField == fromDateTextField && fromDateTextField.text != "" {
dateString = fromDateTextField.text
let date = dateFormatter.date(from: dateString!)
datePickerView.setDate(date!, animated: false)
} else if activeTextField == toDateTextField && activeTextField.text != "" {
dateString = toDateTextField.text
let date = dateFormatter.date(from: dateString!)
datePickerView.setDate(date!, animated: false)
}
UIView.animate(withDuration: 0.3) {
self.datePickerContainerView.frame = CGRect(x: 0,
y: UIScreen.main.bounds.size.height - 250,
width: UIScreen.main.bounds.size.width,
height: 250)
}
return false
}
This worked for me :
Initialise : let picker = UIDatePicker()
and call createToolbar function in viewDidLoad
func createToolbar() {
let toolbar = UIToolbar()
toolbar.sizeToFit()
let doneButton = UIBarButtonItem(title: "Set Date", style: .done, target: nil, action: #selector(showDate))
toolbar.setItems([doneButton], animated: false)
fromDateTextField.inputAccessoryView = toolbar
fromDateTextField.inputView = picker
toDateTextField.inputAccessoryView = toolbar
toDateTextField.inputView = picker
picker.datePickerMode = .date
}
#objc func showDate() {
let dateFormat = DateFormatter()
dateFormat.dateFormat = "dd-MM-yyyy"
let string = dateFormat.string(from: picker.date)
if fromDateTextField.isFirstResponder {
fromDateTextField.text = string
}
if toDateTextField.isFirstResponder {
toDateTextField.text = string
}
self.view.endEditing(true)
}
Thank you #rmmaddy for the inputs :)
class ViewController: UIViewController {
#IBOutlet weak var dateField: UITextField!
var datePicker :UIDatePicker!
override func viewDidLoad() {
super.viewDidLoad()
datePicker = UIDatePicker.init(frame: CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 200))
datePicker.addTarget(self, action: #selector(self.dateChanged), for: .allEvents)
dateField.inputView = datePicker
let doneButton = UIBarButtonItem.init(title: "Done", style: .done, target: self, action: #selector(self.datePickerDone))
let toolBar = UIToolbar.init(frame: CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 44))
toolBar.setItems([UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil), doneButton], animated: true)
dateField.inputAccessoryView = toolBar
}
#objc func datePickerDone() {
dateField.resignFirstResponder()
}
#objc func dateChanged() {
dateField.text = "\(datePicker.date)"
}
}
try this

How I get the keyboard frame before the keyboard appears

I have a button where you get a textfield with a keyboard. At the moment, when I get this textfield, the textfield is in the left corner and the textfield appears. How can I handle this, that the textfield is above my keyboard?
The problem is, that I get the keyboard height after the code in editTextButtonTapped is done. How can I get the height of the keyboard before editTextButtonTapped is called?
// This are my global Variables to create the textfield
var myTextField: UITextField = UITextField(frame: CGRect(x: 0,y: 0, width: 0, height:0))
var firstkeyboardHeight = CGFloat()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
#objc func keyboardWillShow(_ notification: NSNotification) -> Void {
if let keyboardRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as AnyObject).cgRectValue {
let keyboardHeight = keyboardRect.height
firstkeyboardHeight = keyboardHeight
}
}
#IBAction func editTextButtonTapped(_ sender: UIButton) {
myTextField.becomeFirstResponder()
self.myTextField = UITextField(frame: CGRect(x: 0,y: self.firstkeyboardHeight, width: self.view.frame.width, height: 50.0))
self.myTextField.backgroundColor = .green
self.Gestures(addGesture: self.myTextField)
self.view.addSubview(self.myTextField)
}
There is only this set of notifications:
[ UIResponder.keyboardWillShowNotification, UIResponder.keyboardWillHideNotification, UIResponder.keyboardDidShowNotification, UIResponder.keyboardDidHideNotification]
Only inside can obtain the frame values:
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification,
object: nil,
queue: .main) { [weak self] notification in
guard let userInfo = notification.userInfo else {
return
}
let initialValue = userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue,
let finalValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue,
let durationValue = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber,
let curveValue = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
}
So, I don't think that there are any possible allowed ways.
You have several ways to do that. In either case I'd avoid recreating the textfield every time.
let myTextField: UITextField = {
let textfield = UITextField()
textfield.translatesAutoresizingMaskIntoConstraints = false
textfield.backgroundColor = .green
textfield.isHidden = true
return textfield
}()
var bottomConstraint: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(myTextField)
let bottomConstraint = myTextField.bottomAnchor.constraint(equalTo: view.bottomAnchor)
NSLayoutConstraint.activate([
myTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor),
myTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor),
myTextField.heightAnchor.constraint(equalToConstant: 50),
bottomConstraint
])
self.bottomConstraint = bottomConstraint
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
}
#objc func keyboardWillShow(_ notification: NSNotification) -> Void {
guard let keyboardFrame = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
bottomConstraint?.constant = -keyboardFrame.height
}
#IBAction func editTextButtonTapped(_ sender: UIButton) {
myTextField.becomeFirstResponder()
myTextField.isHidden = false
}

I want in one textfield it can be show keyboard and datepicker

I want show keyboard when user click the button on the right side and show the datePicker when user click the textField,
#IBAction func doEditBirthDate(_ sender: UITextField) {
if (txfBirthDate.isEditing){
btnPhotoIdCard.isHidden = false
lblPhotoIdCard.isHidden = false
let mDatePicker: UIDatePicker = UIDatePicker()
mDatePicker.datePickerMode = .date
sender.inputView = mDatePicker
mDatePicker.addTarget(self, action: #selector(birthDateChanged(sender:)), for: .valueChanged)
}else {
btnPhotoIdCard.isHidden = true
lblPhotoIdCard.isHidden = true
}
}
#objc func birthDateChanged(sender: UIDatePicker) {
let mDateFormatter = DateFormatter()
mDateFormatter.locale = Locale(identifier: "en_US_POSIX")
mDateFormatter.dateFormat = "dd MMMM yyyy"
self.txfBirthDate.text = mDateFormatter.string(from: sender.date)
}
thats just show date picker but i want add button on the right side text field and if user click it can show keyboard and text it to textfield
I'm sorry for my english
You've to try this
class yourViewController: UIViewController,UITextFieldDelegate
{
var screenHeight = UIScreen.main.bound.height
var screenWidth = UIScreen.main.bound.width
var dateFormatter = DateFormatter()
var doneButton = UIBarButtonItem()
var cancelButton = UIBarButtonItem()
var timeValueLabel = UIBarButtonItem()
var datePicker : UIDatePicker!
var toolBarDtPicker = UIToolbar()
//MARK:- Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
yourTxtField.delegate = self
}
//TextField Editing Begin Function
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool
{
self.hideKeyboard()
textField.inputView = nil
textfieldEding(sender: textField)
return false
}
//To Hide Keyboard
#objc func hideKeyboard()
{
UIApplication.shared.sendAction(#selector(UIApplication.resignFirstResponder), to: nil, from: nil, for: nil)
}
//DatePicker Function
func datePickerView()
{
if ((self.datePicker != nil) && (self.datePicker.superview != nil))
{
datePicker.removeFromSuperview()
}
self.datePicker = UIDatePicker(frame: CGRect(x: 0, y:
UIScreen.main.bounds.height-200, width: UIScreen.main.bounds.width, height: 200))
self.datePicker.backgroundColor = UIColor.white
self.datePicker.addTarget(self, action: #selector(dateHandler), for: .valueChanged)
self.view.addSubview(datePicker)
toolBarDtPicker = UIToolbar(frame: CGRect(x: 0, y: screenHeight-230, width: screenWidth, height: 30))
toolBarDtPicker.barStyle = .default
toolBarDtPicker.isTranslucent = false
toolBarDtPicker.barTintColor = UIColor.blue
toolBarDtPicker.sizeToFit()
// Adding Button ToolBar
doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(doneClick))
let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
timeValueLabel = UIBarButtonItem(title: " ", style: .plain, target: self, action: nil)
let spaceButton2 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancelClick))
doneButton.tintColor = UIColor.white
timeValueLabel.tintColor = UIColor.white
cancelButton.tintColor = UIColor.white
toolBarDtPicker.setItems([cancelButton, spaceButton, timeValueLabel, spaceButton2, doneButton], animated: true)
toolBarDtPicker.isUserInteractionEnabled = true
self.view.addSubview(toolBarDtPicker)
self.toolBarDtPicker.isHidden = false
datePicker.datePickerMode = .date
dateFormatter.dateStyle = .medium
dateFormatter.dateFormat = "EEEE dd MMM yyyy"
}
#objc func textfieldEding(sender: UITextField)
{
datePickerView()
}
#IBAction func showKeyBoard(_ sender: UIButton!)
{
self.yourTxtField.becomeFirstResponder()
}
//MARK: DatePicker
#objc func dateHandler(sender: UIDatePicker)
{
dateFormatter.dateFormat = "EEEE dd MMM yyyy"
timeValueLabel.title = dateFormatter.string(from: sender.date)
}
#objc func doneClick() //DatePIcker Done Button
{
//Done Button Code
}
//MARK: Cancel Btn
#objc func cancelClick() //DatePIcker Cancel Button
{
if ((self.datePicker != nil) && (self.datePicker.superview != nil))
{
datePicker.removeFromSuperview()
}
toolBarDtPicker.removeFromSuperview()
}
}

Swift passing parameter on #selector

How can I use the same function to handle DatePickerView on 2 different textFields?
To avoid repetition, I would like to set up 2 textFields with a DatePicker. One field is for issue date and the other one for due date.
When I try to add parameter on #objc func doneClick, I got a compiler error ==> Instance member 'doneClick' cannot be used on type 'InvoiceViewController'; did you mean to use a value of this type instead?
import UIKit
class InvoiceViewController: UIViewController, UITextFieldDelegate{
var thePicker = UIDatePicker()
#IBOutlet weak var dateIssueTextField: UITextField!
#IBOutlet weak var dueDateTextField: UITextField!
#objc func doneClick(textField: UITextField!) {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .none
dateIssueTextField.text = dateFormatter.string(from: thePicker.date)
dateFormatter.dateFormat = "yyyy"
let year: String = dateFormatter.string(from: self.thePicker.date)
dateFormatter.dateFormat = "MM"
let month: String = dateFormatter.string(from: self.thePicker.date)
dateFormatter.dateFormat = "dd"
let day: String = dateFormatter.string(from: self.thePicker.date)
let finalDate = year+"-"+month+"-"+day
print(finalDate)
// dateIssueTextField.resignFirstResponder()
textField.resignFirstResponder()
}
#objc func cancelClick() {
dateIssueTextField.resignFirstResponder()
}
func setUpTextFieldPicker(textField: UITextField) {
// DatePicker
self.thePicker = UIDatePicker(frame:CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 216))
self.thePicker.backgroundColor = UIColor.white
self.thePicker.datePickerMode = UIDatePickerMode.date
// dateIssueTextField.inputView = thePicker
// dueDateTextField.inputView = thePicker
textField.inputView = thePicker
// ToolBar
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.isTranslucent = true
toolBar.tintColor = UIColor(red: 92/255, green: 216/255, blue: 255/255, alpha: 1)
toolBar.sizeToFit()
// Adding Button ToolBar
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(InvoiceViewController.doneClick))
let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(InvoiceViewController.cancelClick))
toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
//dateIssueTextField.inputAccessoryView = toolBar
//dueDateTextField.inputAccessoryView = toolBar
textField.inputAccessoryView = toolBar
}
override func viewDidLoad() {
super.viewDidLoad()
setUpTextFieldPicker(textField: dateIssueTextField)
setUpTextFieldPicker(textField: dueDateTextField)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
doneClick cannot have parameter of UITextField type, when it's used as a target to UIBarButtonItem.
You will have to edit more than just that method. The toolbar needs to know to which UITextField it belongs to. So create your custom subclass of UIToolbar, in which you will setup its items, and add a delegate to it which will be sent events on done and cancel pressed:
import UIKit
protocol AccessoryToolbarDelegate: class {
func doneClicked(for textField: UITextField)
func cancelClicked(for textField: UITextField)
}
class AccessoryToolbar: UIToolbar {
fileprivate let textField: UITextField
weak var accessoryDelegate: AccessoryToolbarDelegate?
init(for textField: UITextField) {
self.textField = textField
super.init(frame: CGRect.zero)
self.barStyle = .default
self.isTranslucent = true
self.tintColor = UIColor(red: 92/255, green: 216/255, blue: 255/255, alpha: 1)
self.sizeToFit()
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(doneClicked))
let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancelClicked))
self.setItems([cancelButton, spaceButton, doneButton], animated: false)
self.isUserInteractionEnabled = true
textField.inputAccessoryView = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc fileprivate func doneClicked() {
accessoryDelegate?.doneClicked(for: self.textField)
}
#objc fileprivate func cancelClicked() {
accessoryDelegate?.cancelClicked(for: self.textField)
}
}
Now with this code you can rewrite your old code to:
import UIKit
class InvoiceViewController: UIViewController, AccessoryToolbarDelegate {
var thePicker = UIDatePicker()
#IBOutlet weak var dateIssueTextField: UITextField!
#IBOutlet weak var dueDateTextField: UITextField!
func doneClicked(for textField: UITextField) {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .none
// I am not sure if you want this
textField.text = dateFormatter.string(from: thePicker.date)
// or rather this, but that's up to you to finish
dateFormatter.dateFormat = "yyyy-MM-dd"
let finalDate: String = dateFormatter.string(from: thePicker.date)
print(finalDate)
textField.resignFirstResponder()
}
func cancelClicked(for textField: UITextField) {
textField.resignFirstResponder()
}
func setUpTextFieldPicker(textField: UITextField) {
// DatePicker
self.thePicker = UIDatePicker(frame:CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 216))
self.thePicker.backgroundColor = UIColor.white
self.thePicker.datePickerMode = UIDatePickerMode.date
// dateIssueTextField.inputView = thePicker
// dueDateTextField.inputView = thePicker
textField.inputView = thePicker
// ToolBar
let toolbar = AccessoryToolbar(for: textField)
// let's not forget to set the delegate
toolbar.accessoryDelegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
setUpTextFieldPicker(textField: dateIssueTextField)
setUpTextFieldPicker(textField: dueDateTextField)
}
}

Resources