Button activated when date picker and text field are set in Swift - ios

I want to implement a function that activates the button when the text field and date picker are set.
The value of the date picker goes to the label, and I checked the button by changing the label value.
However, among other functions that I have implemented, if the navigation is popped using a singleton object and the value is saved when it comes back, if the button is checked with a label change, the button is not activated because it is duplicated with the singleton, or even if only the phone number is set. It happens that the button is activated.
So, rather than changing the label, is there a way to catch it when the date picker itself changes the value?
import UIKit
class ThirdViewController: UIViewController {
lazy var dateFormatter: DateFormatter = {
let date = DateFormatter()
date.dateStyle = .medium
date.timeStyle = .none
return date
}()
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var numberTextField: UITextField!
#IBOutlet weak var datePicker: UIDatePicker!
#IBOutlet weak var signUpButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
numberTextField.addTarget(self, action: #selector(editingChanged(_:)), for: .editingChanged)
numberTextField.text = UserInformation.userInformationSingleton.userNumber
dateLabel.text = UserInformation.userInformationSingleton.dateLabel
}
#objc func editingChanged(_ textField: UITextField) {
if textField.text?.count == 1 {
if textField.text?.first == " " {
textField.text = ""
return
}
}
toggleButton()
}
func toggleButton() {
guard
let textField = numberTextField.text, !textField.isEmpty,
dateLabel.text != ""
else {
signUpButton.isEnabled = false
return
}
signUpButton.isEnabled = true
}
#IBAction func datePickerValueChanged(_ sender: UIDatePicker) {
dateLabel.text = dateFormatter.string(from: sender.date)
toggleButton()
}
#IBAction func cancelButton(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
UserInformation.userInformationSingleton.userID = nil
UserInformation.userInformationSingleton.userNumber = nil
UserInformation.userInformationSingleton.dateLabel = nil
}
#IBAction func tappedPreviousButton(_ sender: UIButton) {
self.navigationController?.popViewController(animated: true)
}
#IBAction func tappedSignUpButton(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
}
https://i.stack.imgur.com/a1GVc.png
thanks for reading!!

solved the problem!
func toggleButton() {
guard
let textField = numberTextField.text, !textField.isEmpty,
let label = dateLabel.text, !label.isEmpty
else {
signUpButton.isEnabled = false
return
}
signUpButton.isEnabled = true
}

Related

How to check if UITextField became empty again?

I'm creating a simple log in VC. let's ignore the input validation for both username & password. I just want to enable the UIButton when both username's and password's UITextField is not empty. And whenever any one of them becomes empty, I want the button to be disabled.
#IBAction func typingUserName(_ sender: Any) {
usernameTxtfield.layer.shadowPath = UIBezierPath(rect: usernameTxtfield.bounds).cgPath
usernameTxtfield.layer.shadowRadius = 2
usernameTxtfield.layer.shadowOffset = .zero
usernameTxtfield.layer.shadowOpacity = 0.5
signInIcon.isEnabled = false
}
#IBAction func typingPassword(_ sender: Any) {
passwordTxtfield.layer.shadowPath = UIBezierPath(rect: passwordTxtfield.bounds).cgPath
passwordTxtfield.layer.shadowRadius = 2
passwordTxtfield.layer.shadowOffset = .zero
passwordTxtfield.layer.shadowOpacity = 0.5
signInIcon.isEnabled = false
}
#IBAction func usernameTxtFieldEditingChnged(_ sender: Any) {
usernameTxtfield.layer.shadowRadius = 0
usernameTxtfield.layer.shadowOffset = .zero
usernameTxtfield.layer.shadowOpacity = 0
}
#IBAction func passwordEditingChaned(_ sender: Any) {
passwordTxtfield.layer.shadowRadius = 0
passwordTxtfield.layer.shadowOffset = .zero
passwordTxtfield.layer.shadowOpacity = 0
signInIcon.isEnabled = true
}
#IBAction func signInClicked(_ sender: Any) {
performSegue(withIdentifier: "welcomeVC", sender: signInIcon)
}
As you can see, I'm enabling the button only after the password textfield EditingChanged has been triggered.
You can observe event .editingChanged.
passwordTxtfield.addTarget(self, action: #selector(passwordEditingChaned), for: .editingChanged)
usernameTxtfield.addTarget(self, action: #selector(usernameTxtFieldEditingChnged), for: .editingChanged)
And then add check in both methods:
signInIcon.isEnabled = passwordTxtfield.text?.isEmpty == false && usernameTxtfield.text?.isEmpty == false
You have to implement something like this:
if usernameTxtfield.text != "" && passwordTxtfield.text != "" {
signInIcon.isEnabled = true
} else {
signInIcon.isEnabled = false
}
You add this piece of code in the action of each UITextField and your are good to go
Connect outlets of textFields and Button to ViewController Class.
#IBOutlet private weak var usernameTxtfield: UITextField!
#IBOutlet private weak var passwordTxtfield: UITextField!
#IBOutlet private weak var signInButton: UIButton!
then write your setup function
private func setupTextFields() {
// write your TextFields custom setup ...
// also add delegate lines
usernameTxtfield.delegate = self
passwordTxtfield.delegate = self
}
your setupTextFields function to viewDidLoad of VC and also set your button isEnable = false
override func viewDidLoad() {
super.viewDidLoad()
setupTextFileds()
setSignInButton(isEnable: false)
}
and also write func to get textFields.
private func getTextFields() -> [UITextField] {
return [usernameTxtfield, passwordTxtfield]
}
also we need to check if these are valid.
private func isInputsValid() -> Bool {
var isValid: Bool = true
let inputs: [UITextField] = getTextFields()
if let input = inputs.first(where: { $0.text?.count == 0 }) {
debugPrint("\(input) is not valid.")
isValid = false
}
return isValid
}
also add func to set Button
private func setSignInButton(isEnable: Bool) {
signInButton.isEnabled = isEnable
}
then write TextField delegate func to understand inputs are changing and change condition of button.
extension ViewController: UITextFieldDelegate {
func textFieldDidChangeSelection(_ textField: UITextField) {
setSignInButton(isEnable: isInputsValid())
}
}
I hope it was helpful :)

Is it possible to make the borderColor of the textField inside the tableView change as the text color of the textField changes?

I want to change borderColor from lightGray to black when textField is firstResponder. And if the textField resigns again, I want to change it to gray. TextField's textColor changes color automatically without tableView's reload, but borderColor doesn't. Is there a way to change the color by observing the values ​​without reloading the tableView?
class ChangePasswordViewController: UITableViewController {
#IBOutlet weak var newPasswordTextField: RoundTextField!
#IBOutlet weak var checkingNewPasswordTextField: RoundTextField!
#IBOutlet weak var explainLabel: UILabel!
#IBOutlet weak var changePwButton: FillRoundButton!
let viewModel = ChangePasswordViewModel()
override func viewDidLoad() {
super.viewDidLoad()
hideKeyboardWhenTappedAround()
newPasswordTextField.tag = 0
checkingNewPasswordTextField.tag = 1
configureNotificationObservers()
}
func configureNotificationObservers() {
newPasswordTextField.addTarget(self, action: #selector(textDidChange(sender:)), for: .editingChanged)
checkingNewPasswordTextField.addTarget(self, action: #selector(textDidChange(sender:)), for: .editingChanged)
}
// MARK: - Actions
#objc func textDidChange(sender: RoundTextField) {
if sender == newPasswordTextField {
viewModel.pwValue = sender.text
} else {
viewModel.confirmPwValue = sender.text
}
updateForm()
}
}
extension ChangePasswordViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
if textField.tag == 0 {
viewModel.pwValue = textField.text ?? ""
} else {
viewModel.confirmPwValue = textField.text ?? ""
}
}
}
extension ChangePasswordViewController: FormViewModel {
func updateForm() {
changePwButton.isEnabled = viewModel.pwFormIsValid
newPasswordTextField.layer.borderColor = UIColor.yellow.cgColor
newPasswordTextField.textColor = .yellow
}
}
enter image description here

Is there a way to Interrupt viewWillDisappear in iOS?

I'm looking for a way to interrupt viewWillDisappear when the back button is pressed (on UINavigationController), AND a certain data condition occurs, but I cannot find any way to logically keep the user on the view until my conditions have been met.
import UIKit
class DetailViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var serialNumberField: UITextField!
#IBOutlet weak var valueField: UITextField!
#IBOutlet weak var dateLabel: UILabel!
// adds item.name to top of detail view
var item: Item! {
didSet {
navigationItem.title = item.name
}
}
// ************************************
// MARK: Number / Date formatters
// ************************************
let numberFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
return formatter
}()
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
return formatter
}()
// ************************************
// MARK: viewWillAppear
// ************************************
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
nameField.text = item.name
serialNumberField.text = item.serialNumber
valueField.text = numberFormatter.string(from: NSNumber(value: item.valueInDollars))
dateLabel.text = dateFormatter.string(from: item.dateCreated)
}
// ************************************
// MARK: viewWillDisappear
// ************************************
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// clear first responder...
// this creates smoother transition if the
// keyboard is visible when the back button is pressed.
view.endEditing(true)
// save changes to item
item.name = nameField.text ?? ""
item.serialNumber = serialNumberField.text
if let valueText = valueField.text, let value = numberFormatter.number(from: valueText) {
// item.valueInDollars = value.intValue
// Rounding value of item before passing it back.
item.valueInDollars = Int(round(Float(truncating: value)))
} else {
item.valueInDollars = 0
}
}
// *************************************
// MARK: Resign keyboard with return key
// *************************************
// resign keyboard when return pressed from within textFields
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
// ************************************
// MARK: backGroundTapped TapGesture
// ************************************
#IBAction func backgroundTapped(_ sender: UITapGestureRecognizer) {
view.endEditing(true)
}
}

Disable selection in TextField for selected Date in Swift

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)
}

datepicker update value in two differents UIButton

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

Resources