I have a custom UIView component that has a UITextField, UIImageView and another UIView.
I need to change the behavior of the UITextField to display a UIDatePicker when it's tapped. This is the code I'm trying to execute but for some reason the datePicker doesn't open when it's tapped:
class TextFieldWithFeedback : UIView {
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var underlineView: UIView!
override func draw(_ rect: CGRect) {
let datePicker = UIDatePicker()
datePicker.datePickerMode = .date
self.textField.inputView = datePicker
}
}
When I used the same code on my ViewController (code below) it worked:
override func viewDidLoad() {
super.viewDidLoad()
let datePicker = UIDatePicker()
datePicker.datePickerMode = .date
customView.textField.inputView = datePicker
}
Now, this solution works but it's not ideal because I have 8 of those custom views and it would be better to set it once instead of 8 times.
Which method of the View class should I use to set the datePicker up? Since draw doesn't work.
Also, how can I attach a function to modify my UITextField value when the user picks a date?
Managed to solve it like this:
//To control whether or not to use the DatePicker, easily customized on the Storyboard
#IBInspectable var useDatePicker: Bool = false
#IBAction func textFieldEditing(_ sender: UITextField) {
self.textFieldStartedEditing(sender: sender)
}
func textFieldStartedEditing(sender: UITextField) {
if useDatePicker {
let datePickerView:UIDatePicker = UIDatePicker()
datePickerView.datePickerMode = .date
sender.inputView = datePickerView
datePickerView.addTarget(self, action: #selector(self.datePickerValueChanged), for: UIControlEvents.valueChanged)
}
}
func datePickerValueChanged(sender: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/YYYY"
self.textField.text = dateFormatter.string(from: sender.date)
}
All of the code above inside my customView class.
If anyone else is having trouble with this and needs help feel free to comment, I promise I'll be more helpful than the people who commented on the question :)
what you need to do is addTarget to your Textfield and then in a method Show the UIPickerView
self.textField.addTarget(self, action: #selector(ViewController.openDatesPicker), for: .touchDown)
You should also set tag for your textfield so you can identify it later in the code. Also you should implement this method which stops the keyboard from showing up
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField.tag == 2 {
return true
} else {
return false
}
}
Related
In my swift app I've two textfields and as inputView is UIDatePicker().
Here's my code:
class ViewController: UIViewController {
#IBOutlet weak var textField1: UITextField!
#IBOutlet weak var textField2: UITextField!
var timePicker = UIDatePicker()
var timeString = String()
override func viewDidLoad() {
super.viewDidLoad()
timePicker.datePickerMode = .time
}
#objc func formatData(_ datePickerView: UIDatePicker) {
let timeFormatter = DateFormatter()
timeFormatter.timeStyle = .short
timeString = timeFormatter.string(from: datePickerView.date)
}
#IBAction func textFieldChanged(_ sender: UITextField) {
sender.inputView = timePicker
timePicker.addTarget(self, action: #selector(formatData(_:)), for: .valueChanged)
sender.text = timeString
}
}
By this way to change value into text field I've to deselect it and then rettapping on it the value appear.
But it's not correct.
first you take the IBAction of that textfield which you want to change the input view and name should be of your IBAction method didBeginEditing and in that method you can the input view of that textfield which you want to change
My save button is hidden with the function saveBtnHidden() in the code below. However, the save button won't reappear when text is typed into the text field. I have tried multiple solutions similar to this. Every time that I type into the text field, the save button just won't show up.
import UIKit
class TableViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var saveBtn: UIButton!
#IBOutlet var nicknameField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
saveBtnHidden()
}
func saveBtnHidden() {
if (nicknameField.text?.isEmpty ?? true) {
// is empty
saveBtn.isHidden = true
} else {
saveBtn.isHidden = false
}
}
#IBAction func saveBtnPressed(_ sender: Any) {
performSegue(withIdentifier: "nextPage", sender: nil)
}
}
You are getting this error because your function saveBtnHidden() is only called once in viewDidLoad(). It does not get called again when the text in your text field changes. To detect when text changes, you will need to add a target to your text field that calls a function when it changes (.editingChanged) like this:
nicknameField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
Then in the textFieldDidChange call your saveBtnHidden() function:
func textFieldDidChange(_ textField: UITextField) {
saveBtnHidden()
}
Code adapted from: How do I check when a UITextField changes?
Use delegate to be notify of any change. Delegates is a key part of iOS development and Apple's framework.
class TableViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var saveBtn: UIButton!
#IBOutlet var nicknameField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
saveBtnHidden()
nicknameField.delegate = self
}
func textFieldDidChange(_ textField: UITextField) {
saveBtnHidden()
}
// More of your source code below...
I want to allow the user to change gravity based on their input in gravity text field and pass the input from view controller to a SKScene (PhysicsScene) but I don't know how to reload scene so that it would use the custom gravity by the user instead of its default value.
view of simulator
class SimulationViewController: UIViewController, UITextFieldDelegate {
var sceneNode : PhysicsScene?
#IBOutlet weak var heightTextField: UITextField!
#IBOutlet weak var massTextField: UITextField!
#IBOutlet weak var gravityTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
sceneNode = PhysicsScene(size: view.frame.size)
gravityTextField.delegate = self
if let view = self.view as! SKView? {
view.presentScene(sceneNode)
view.ignoresSiblingOrder = true
view.showsPhysics = true
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField.tag == 0 {
if let text = textField.text {
sceneNode?.customGravity = Double(text)!
print(text)
}
}
return true
}
Please help! Thank you!
You have 2 options, make a property in your PhysicsScene class
var customGravity : Double = 0{ didSet { physicsWorld.gravity = {CGPoint(x:0 y:CGFloat(customGravity)}}
or drop custom gravity and set it directly
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField.tag == 0 {
if let text = textField.text {
sceneNode?.customGravity = physicsWorld.gravity = {CGPoint(x:0 y:CGFloat(Double(text)!)}
print(text)
}
}
return true
}
I do not have XCode available to check my typos, you may have to unwrap some variables
I have a date picker I am using to set the value of a text field. It works great except that it only works the 2nd time that I tap into the text field, that is, the first time I tap the text field it shows me the standard keyboard. Then if I tap out of the textfield and back into it, it shows the datepicker. I've tried different variations of it, but it either doesn't work at all, or I get the same problem. This is how I set it up:
class ExpDateTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var textField: UITextField!
#IBAction func textFieldEditing(sender: UITextField) {
let datePickerView:UIDatePicker! = UIDatePicker()
datePickerView.datePickerMode = UIDatePickerMode.date
sender.inputView = datePickerView
datePickerView.addTarget(self, action: #selector(ExpDateTableViewCell.datePickerValueChanged), for: UIControlEvents.valueChanged)
}
func datePickerValueChanged(sender:UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = DateFormatter.Style.medium
dateFormatter.timeStyle = DateFormatter.Style.none
textField.text = dateFormatter.string(from: sender.date)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
The problem is this code:
#IBAction func textFieldEditing(sender: UITextField) {
// ...
sender.inputView = datePickerView
}
You are not setting the text field's inputView to the picker view until after the first time we have started editing. Thus we get the effect you correctly described: the second time we edit, the picker view is present.
You need to set the text field's inputView much earlier, such as in awakeFromNib, before the user has a chance to edit the text field for the first time.
First make UIDatepickerview as a inputview for textfield
Add this code inside the textfield action
#IBAction func textFieldEditing(sender: UITextField) {
let datePickerView:UIDatePicker = UIDatePicker()
datePickerView.datePickerMode = UIDatePickerMode.Date
sender.inputView = datePickerView
datePickerView.addTarget(self, action: #selector(ViewController.datePickerValueChanged), forControlEvents: UIControlEvents.ValueChanged)
}
In my app i have added 2 labels and 2 buttons, the buttons are hooked to a function that pops a datepicker in popovercontroller, when user select a date the corresponding label should change.
Everything work except the labels are updated together, what I want is update only the relevant label, i.e. when when button1 is pressed label1 should recieve datepicker date and vice versa.
Here is my code:
import UIKit
class PopoverTestVC: UIViewController, UIPopoverPresentationControllerDelegate {
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var dateLabel2: UILabel!
#IBOutlet weak var selectButton: UIButton!
#IBOutlet weak var selectButton2: UIButton!
#IBOutlet var datePicker: UIDatePicker!
var dateString = ""
let vc = UIViewController()
override func viewDidLoad() {
super.viewDidLoad()
datePicker.timeZone = NSTimeZone(abbreviation: "GMT")
//datePicker.addTarget(self, action: Selector("datePicked:"), forControlEvents: UIControlEvents.ValueChanged)
}
func datePicked(sender: UIDatePicker) {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd MMM yyyy HH:mm"
dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT")
dateLabel.text = dateFormatter.stringFromDate(datePicker.date)
}
func datePicked2(sender: UIDatePicker) {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd MMM yyyy HH:mm"
dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT")
dateLabel2.text = dateFormatter.stringFromDate(datePicker.date)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func popUp(sender: UIButton)
{
let fr = datePicker.frame
vc.view.frame = fr
vc.view.addSubview(datePicker!)
vc.view.backgroundColor = UIColor.greenColor()
vc.preferredContentSize = datePicker.frame.size
vc.modalPresentationStyle = UIModalPresentationStyle.Popover
if sender == selectButton
{
datePicker.addTarget(self, action: Selector("datePicked:"), forControlEvents: UIControlEvents.ValueChanged)
}
else if sender == selectButton2
{
datePicker.addTarget(self, action: Selector("datePicked2:"), forControlEvents: UIControlEvents.ValueChanged)
}
let popOverPC = vc.popoverPresentationController
vc.popoverPresentationController?.sourceRect = sender.frame
vc.popoverPresentationController?.sourceView = view
popOverPC!.permittedArrowDirections = UIPopoverArrowDirection.Any
popOverPC!.delegate = self
presentViewController(vc, animated: true, completion: nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle
{
return UIModalPresentationStyle.None
}
}
Appreciate any help or suggestion to do that in better way as the app might have several outlets that use the same datepicker.
I'd move your date picker to a custom class (this also lets you use a xib to do some cool design stuff) call this from your view controller setting a var to the button that called the date picker. Create a protocol in the date picker and implement the delegate in the parent view controller populating whatever labels you require based on the button that called the date picker.
For example, in my app I have several text fields that must contain dates. When the user begins to edit the text field I launch a custom popover from textFieldShouldBeginEditing and assign the text field to a var.
The parent view controller is set as the delegate to a protocol in my custom date picker:
// MARK: - Textfield delegate
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
textField.backgroundColor = UIColor.whiteColor()
if (textField.tag == 500 || textField.tag == 501)
{
activeTextField = textField
let vc = BGSDatePickerVC(nibName: "BGSDatePickerVC", bundle: nil)
vc.modalPresentationStyle = UIModalPresentationStyle.Popover
if textField.tag == 500{
vc.popoverPresentationController?.sourceView = viewMarkerFromDate
}else
{
vc.popoverPresentationController?.sourceView = viewMarkerToDate
}
vc.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.Up
vc.preferredContentSize = CGSizeMake(vc.view.frame.width, vc.view.frame.height)
vc.delegate = self
self.presentViewController(vc, animated: true, completion: nil)
return false
}
return true
}
The protocol in my custom date picker popover:
protocol BGSDatePickerVCProtocol
{
func bgsDatePickerUse(date: NSDate, strDate: String)
}
class BGSDatePickerVC: UIViewController {
var delegate:BGSDatePickerVCProtocol! = nil
and the implementation in the calling view controller:
// MARK: - Delegates
// MARK: BGSDatePickerVCProtocol
func bgsDatePickerUse(date: NSDate, strDate: String)
{
if activeTextField.tag == 500
{
dateSelectedFrom = date
}
if activeTextField.tag == 501
{
dateSelectedTo = date
}
activeTextField.text = strDate
}