I am trying to link UIDatePicker to a text field so that if I select a particular date, it gets in the textField. I am getting errors
import UIKit
class tiViewController: UIViewController {
#IBOutlet weak var txtDatePicker: UITextField!
let datePicker = UIDatePicker()
override func viewDidLoad() {
super.viewDidLoad()
showDatePicker()
}
func showDatePicker(){
//Formate Date
datePicker.datePickerMode = .date /* ERROR 1
- Value of type 'UIDatePicker' has no member 'datePickerMode'*/
//ToolBar
let toolbar = UIToolbar();
toolbar.sizeToFit()
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(donedatePicker));
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancelDatePicker));
toolbar.setItems([doneButton,spaceButton,cancelButton], animated: false)
txtDatePicker.inputAccessoryView = toolbar
txtDatePicker.inputView = datePicker
}
#objc func donedatePicker(){
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy"
txtDatePicker.text = formatter.string(from: datePicker.date)
self.view.endEditing(true)
}
#objc func cancelDatePicker(){
self.view.endEditing(true) /* ERROR 2 - Value of type 'UIDatePicker' has no member 'date'*/
}
it shows above ERROR1 AND ERROR2
I have also linked tiviewcontroller to textfield outlet properly -
Related
I'm trying to implement a button to my navbar which should call the function addTap.
Unfortunately though, when I click the add button in the simulator, nothing happens. I put a print statement in the addTap function but I doesn't get called, meaning the function never gets called.
Have any ideas?
class ViewController: UIViewController {
var names : [String] = []
let TableView = UITableView() // view
var addButton: UIBarButtonItem = UIBarButtonItem(title: "Add", style: .done, target: self, action: #selector(addTap))
override func loadView() {
super.loadView()
setupComponents()
setupTableView()
}
func setupComponents() {
self.title = "Core Data"
self.navigationItem.rightBarButtonItem = self.addButton
}
func setupTableView() {
view.addSubview(TableView)
TableView.translatesAutoresizingMaskIntoConstraints = false
TableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
TableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
TableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
TableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
#objc func addTap() {
print("reached")
let alert = UIAlertController(title: "New Friend", message: "Add the name of your friend", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Add Now", style: .default) { [unowned self] action in
guard let textField = alert.textFields?.first, let nameToSave = textField.text else { return }
self.names.append(nameToSave)
self.TableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
alert.addTextField()
alert.addAction(saveAction)
alert.addAction(cancelAction)
present(alert, animated: true)
}
}
Your issue is with the construction of your UIBarButtonItem. You are trying to access self before self exists.
If you construct it like this you will find that it works.
func setupComponents() {
self.title = "Core Data"
let addButton: UIBarButtonItem = UIBarButtonItem(title: "Add", style: .done, target: self, action: #selector(addTap))
self.navigationItem.rightBarButtonItem = addButton
}
Or if you need access to the button instance you can create the UIBarButtonItem lazily and replace the declaration in your class with the following:
lazy var addButton: UIBarButtonItem = {
UIBarButtonItem(title: "Add", style: .done, target: self, action: #selector(addTap))
}()
How to implement this code inside a Protocol in order to achieve Protocol Oriented Programing? One big problem is settling the #objc method, which are not allowed in protocols. Second one is this code is now used for UITextField inside many cells, that's why I extend UIView and why I corrected endediting
original code found here
//used inside a cell with a UITextField
extension UIView {
func toolBar() -> UIToolbar{
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.isTranslucent = true
toolBar.barTintColor = UIColor.init(red: 0/255, green: 25/255, blue: 61/255, alpha: 1)
let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let buttonTitle = "Done"
let cancelButtonTitle = "Cancel"
let doneButton = UIBarButtonItem(title: buttonTitle, style: .done, target: self, action: #selector(onClickDoneButton))
let cancelButton = UIBarButtonItem(title: cancelButtonTitle, style: .plain, target: self, action: #selector(onClickCancelButton))
doneButton.tintColor = .white
cancelButton.tintColor = .white
toolBar.setItems([cancelButton, space, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
toolBar.sizeToFit()
return toolBar
}
#objc func onClickDoneButton(){
// view.endEditing(true)
self.endEditing(true)
}
#objc func onClickCancelButton(){
// view.endEditing(true)
self.endEditing(true)
}
}
EDIT on Sandeep Bhandari's answer this is the working implementation
first I added a file with
extension UIView: ToolBarProtocol {}
then inside my cell now I have:
let selDone = #selector(onClickDoneButton)
let selCancel = #selector(onClickCancelButton)
self.cellTextfield.inputAccessoryView = toolBar(with: selDone, cancelSeclector: selCancel)
#objc func onClickDoneButton() {
self.endEditing(true)
}
#objc func onClickCancelButton() {
self.endEditing(true)
}
Best you can do I guess is
protocol ToolBarProtocol where Self: UIView {
func toolBar(with doneSelector: Selector?, cancelSeclector: Selector?) -> UIToolbar
}
extension ToolBarProtocol {
func toolBar(with doneSelector: Selector?, cancelSeclector: Selector?) -> UIToolbar{
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.isTranslucent = true
toolBar.barTintColor = UIColor.init(red: 0/255, green: 25/255, blue: 61/255, alpha: 1)
let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let buttonTitle = "Done"
let cancelButtonTitle = "Cancel"
let doneButton = UIBarButtonItem(title: buttonTitle, style: .done, target: self, action: doneSelector)
let cancelButton = UIBarButtonItem(title: cancelButtonTitle, style: .plain, target: self, action: cancelSeclector)
doneButton.tintColor = .white
cancelButton.tintColor = .white
toolBar.setItems([cancelButton, space, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
toolBar.sizeToFit()
return toolBar
}
}
In nutshell You can't provide a default extension to #objc protocol.
What does it have to do with this problem?
You cant provide a default extension to your done and cancel button selectors, because they will need to be annotated with #objc (Typical selector signature is selector(#objc method)) and as soon as you add #objc to them then compiler will give you a compilation error
#objc can only be used with members of classes, #objc protocols, and
concrete extension of classes
so to make your protocol compatible with #objc function you will endup making it #objc protocol, and if you make a protocol #objc you cant provide a default extension to that
For more details refer: Swift protocol extension in Objective-C class
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(buttonClicked(sender:)))
#objc func buttonClicked(sender: UIBarButtonItem) {
print("Hello")
}
That's the code for my UIBarButton but when I click on it it doesn't print "Hello", what could be the problem?
EDIT: Here are my viewcontroller
It simply control a button that when it's clicked show the spinner with its control, but as I said before the button on toolbar doesn't work
class FilterViewController: UIViewController {
var search: Search?
let categoriesSpinnerDelegate = CategoriesPickerDelegate()
#IBOutlet weak var generalSpinner: UIPickerView!
#IBOutlet weak var categoriesButton: UIButton!
#IBOutlet weak var categoryRow: UIView!
var doneButton: UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.topViewController?.title = "Filtri"
// Set border and click action
self.categoryRow.layer.borderWidth = 1
self.categoryRow.layer.borderColor = Raccoltacase.lightGray.cgColor
self.categoryRow.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.buttonClicked(sender:))))
// Create toolbar and attach it to pickerView
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
//toolBar.tintColor = UIColor(red: 76/255, green: 217/255, blue: 100/255, alpha: 1)
toolBar.sizeToFit()
self.doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(buttonClicked(sender:)))
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.plain, target: self, action: nil)
toolBar.setItems([cancelButton, spaceButton, doneButton!], animated: false)
toolBar.isUserInteractionEnabled = true
generalSpinner.addSubview(toolBar)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func categoriesButtonClick(_ sender: UIButton) {
self.generalSpinner.showsSelectionIndicator = true
self.generalSpinner.dataSource = categoriesSpinnerDelegate
self.generalSpinner.delegate = categoriesSpinnerDelegate
self.generalSpinner.isHidden = false
}
#objc func buttonClicked(sender: UIBarButtonItem) {
print("Hello")
}
}
Screen
Assuming that you are showing the picker sometimes (and dismissing it when user presses the done button), here is my solution:
Add a UITextField to the view in storyboard (and make the tintColor transparent (clearColor))
Add the UIToolbar as inputAccessoryView to the UITextField
Add the UIPickerView as inputView to the toolbar (Also see the note below)
Below is a sample code:
override func viewDidLoad() {
super.viewDidLoad()
textField.inputView = generalSpinner
textField.inputAccessoryView = getToolbar()
}
func getToolbar() -> UIToolbar {
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 40))
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
self.doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(buttonClicked(sender:)))
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.plain, target: self, action: nil)
toolBar.setItems([cancelButton, spaceButton, doneButton!], animated: false)
toolBar.isUserInteractionEnabled = true
return toolBar
}
Note: In case the UIPickerView is already in the storyboard (or a subview of another view), make sure to remove it in the first line of viewDidLoad (as shown below):
override func viewDidLoad() {
super.viewDidLoad()
generalSpinner.removeFromSuperview()
textField.inputView = generalSpinner
textField.inputAccessoryView = getToolbar()
}
SOLVED
When i use pickerView.addSubView(toolbar) the toolbar was placed behind the pickerview and so it was no clickable. I solved it adding the toolbar manually from storyboard with General Spinner.top = Picker Toolbar.bottom
I am new in swift. On executing this app login screen goes to sign in.On selecting "Already have account sign up".You get to sign up view. In the sign up trying to develop the picker view.These are two issues arise while developing picker view
1) picker view should be hidden on loading view controller .but picker view is visible as view controller appear?
2) In date of birth pickview when a done button on date picker view is selected the app crash .but it should display the corresponding date in a text field?
func showDatePicker(){
//Formate Date
datePicker.datePickerMode = .date
//ToolBar
let toolbar = UIToolbar();
toolbar.sizeToFit()
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.bordered, target: self, action: "donedatePicker")
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.bordered, target: self, action: "cancelDatePicker")
toolbar.setItems([doneButton,spaceButton,cancelButton], animated: false)
dateTextField.inputAccessoryView = toolbar
dateTextField.inputView = datePicker
}
func donedatePicker(){
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy"
dateTextField.text = formatter.string(from: datePicker.date)
self.view.endEditing(true)
}
func cancelDatePicker(){
self.view.endEditing(true)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
pickerView.delegate=self
pickerView.dataSource=self
genderTextField.inputView=pickerView
showDatePicker()
}
How to avoid this crash. You can download the project from this link https://drive.google.com/file/d/1a6Pmw2gVDwLP9grL8tG72NBx-tKwZozI/view?usp=sharing
you miss few things in view did load I had updated the code
override func viewDidLoad() {
super.viewDidLoad()
pickerView.delegate=self
pickerView.dataSource=self
genderTextField.inputView = pickerView
genderTextField.delegate = self
dateTextField.delegate = self
}
Assign the text field delegate and implement the function textFieldShouldBeginEditing function for showing the Date Picker
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField.tag == 1 { //Date Text Field
showDatePicker()
}
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
I had assigned the tag value of Date Text Field from the storyboard and hide the picker view from the storyboard. It will solve your both points
First you need to give delegate to UITextField i.e
dateTextField.delegate = self
and then you need to confirm with UITextFieldDelegate method with sign up class i.e
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == dateTextField {
self.showDatePicker()
}
}
Use this above code may it helps you.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
pickerView.delegate=self
pickerView.dataSource=self
genderTextField.inputView=pickerView
showDatePicker()
}
The mistake is here. When the view is load in viewDidLoad, date picker is show itself. So you must add a action for click to show you picker view and call it in there.
For example
#objc fileprivate func showIt(_ sender:UIButton){
showDatePicker()
}
Also your action must be set wit #selector
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.bordered, target: self, action: "donedatePicker")
changed to
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.bordered, target: self, action: #selector(donedatePicker))
And for error #1 you must be make firstResponder UITextField somehow, you must resign it.
Can you add this function for textFieldDelegate.
extension YourViewController: UITextFieldDelegate{
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField.isFirstResponder{
showDatePicker()
}
}
}
you should b calling this showDatePicker() on a tap at date textfield.
func textFieldDidBeginEditing(_ textField: UITextField) {
let datePicker = UIDatePicker()
if startDateTxt == textField{
textField.inputView = datePicker
datePicker.addTarget(self, action: #selector(showDatePicker()), for: .valueChanged)
}
}
and for 2nd error:
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.bordered, target: self, action: #selector(donedatePicker))
I got a custom UIPickerView from github (https://github.com/bendodson/MonthYearPickerView-Swift") and now I'm trying to connect it to a textfield with no luck. I managed to do it with the standard UiDatePicker using .addTarget and .valueChanged methods, but with this custom one addTarget throws an error. Now I only manage to get the textfield's inputView to the custom PickerView, but not save the input using my "Done" button that I created. What is it that I'm missing?
lazy var ExpireDatetextfeild: UITextField = {
let tf = LeftPaddedTextFeild()
tf.placeholder = "MM/YY"
tf.translatesAutoresizingMaskIntoConstraints = false
tf.addTarget(self, action: #selector(textfeildediting), for: .editingDidBegin)
return tf
}()
let DatePickerView: MonthYearPickerView = MonthYearPickerView()
func textfeildediting() {
let DatePickerView: MonthYearPickerView = MonthYearPickerView()
DatePickerView.backgroundColor = .white
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
toolBar.backgroundColor = UIColor.white
toolBar.sizeToFit()
let donebutton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(DoneFunc))
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.plain, target: self, action: #selector(cancelFunc))
toolBar.setItems([cancelButton, spaceButton, donebutton], animated: false)
toolBar.isUserInteractionEnabled = true
ExpireDatetextfeild.inputAccessoryView = toolBar
ExpireDatetextfeild.inputView = DatePickerView
}
func cancelFunc(sender: UIBarButtonItem) {
DatePickerView.isHidden = true
print("DatePickerPrint")
}
func DoneFunc(sender: UIBarButtonItem) {
DatePickerView.onDateSelected = { (month: Int, year:Int) in
let Yearstring = String(format: "%02d/%d", month, year)
print(Yearstring)
}
}