Hi i'm new with Swift programming.
What im trying to do is Disable my button (signIn) in viewDidLoad and only enable when the textfields have text in them. Here's what i've achieved so far. (not much though!)
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
signIn.isEnabled = false
// Do any additional setup after loading the view, typically from a nib.
}
#IBOutlet weak var emailtxt: UITextField!
#IBOutlet weak var passwordtxt: UITextField!
#IBOutlet weak var signIn: UIButton!
I need help to create a function in signIn that keeps button disabled until text fields (emailtxt & passwordtxt) have text in them and then proceed.
Glad if anyone can sort me.
Thanks in advance!
First add these for all of your textFields in viewDidLoad():
emailtxt.addTarget(self, action: #selector(textFieldDidChange(_:)),
for: UIControlEvents.editingChanged)
passwordtxt.addTarget(self, action: #selector(textFieldDidChange(_:)),
for: UIControlEvents.editingChanged)
Then use this:
#objc func textFieldDidChange(_ textField: UITextField) {
self.buttonIsEnabled()
}
func buttonIsEnabled() {
var buttonIsEnabled = true
defer {
self.signIn.isEnabled = buttonIsEnabled
}
guard let emailtxt = self.emailtxt.text, !emailtxt.isEmpty else {
addButtonIsEnabled = false
return
}
guard let passwordtxt = self. passwordtxt.text, ! passwordtxt.isEmpty else {
addButtonIsEnabled = false
return
}
}
I use this way in my codes and it works well.
Even you can add more methods for additional checking to buttonIsEnabled, like:
self.checkEmailIsValid(for: emailtxt)
Of course you should handle this method before:
func checkEmailIsValid(for: String) {
//...
}
Set ViewController as delegate for emailtxt and passwordtxt like this,
override func viewDidLoad() {
super.viewDidLoad()
signIn.isEnabled = false
emailtxt.delegate = self
passwordtxt.delegate = self
// Do any additional setup after loading the view, typically from a nib.
}
Conform your ViewController to UITextFieldDelegate and enable/disable as the text input is finished,
extension ViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
if emailtxt.text?.isEmpty == false && passwordtxt.text?.isEmpty == false {
signIn.isEnabled = true
} else {
signIn.isEnabled = false
}
}
}
Here is the fix for your code you shared.
import UIKit
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
extension SignInVC: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
if emailtxt.text?.isEmpty == false && passwordtxt.text?.isEmpty == false {
signIn.isEnabled = true
} else {
signIn.isEnabled = false
}
}
}
class SignInVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
signIn.isEnabled = false
emailtxt.delegate = self
passwordtxt.delegate = self
self.hideKeyboardWhenTappedAround()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var emailtxt: UITextField!
#IBOutlet weak var passwordtxt: UITextField!
#IBOutlet weak var signIn: UIButton!
}
What I would do is create an IBAction from one of your text fields, and set the event to Editing Changed:
The code should look like this:
#IBAction func textFieldEditingDidChange(_ sender: UITextField) {
}
You can then connect that same outlet to both of your text fields by dragging from the outlet to your remaining field. If you've connected both correctly, clicking on the circle to the left of your IBAction should show two text fields:
The action will now be fired every time text changes in either of your fields.
Then, at the top of the file, I'd create a computed property that returns false unless there is something in both fields:
var shouldEnableButton: Bool {
guard let text1 = textField1.text, let text2 = textField2.text else {
return false
}
return text1.isEmpty && text2.isEmpty ? false : true
}
Finally, we add shouldEnableButton to our IBAction:
#IBAction func textFieldEditingDidChange(_ sender: UITextField) {
button.isEnabled = shouldEnableButton
}
Important
When you connect your second text field to the outlet, it will incorrectly assign Editing Did End as its event:
Delete this event and click and drag from Editing Changed to your IBAction:
Use SwiftValidator
https://github.com/SwiftValidatorCommunity/SwiftValidator
by this, you will set validation of email & password like below
import SwiftValidator
let validator = Validator()
validator.registerField(emailTextField, errorLabel: emailErrorLabel, rules: [RequiredRule(), EmailRule(message: "Invalid email")])
// MARK: - ValidationDelegate
extension ViewController: ValidationDelegate {
func validationSuccessful() {
self.loginUser()
}
func validationFailed(_ errors:[(Validatable ,ValidationError)]) {
for (field, error) in errors {
//Handle as per need - show extra label - shake view etc
/*
if let field = field as? UITextField {
Utilities.shakeTheView(shakeView: field)
}
error.errorLabel?.text = error.errorMessage
error.errorLabel?.isHidden = false
*/
}
}
}
Related
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 :)
Can not get the the fields to edit in the order of the tags. In this scenario I have four fields on a screen in a two columns and two rows. I want to be able to be able to edit down column 1 and the column2. By default it goes across row 1 then to row2.
This is part of much larger project I am working on. This is a critical feature as there are quite a few more fields (more than 20). I've tried using TextDidEndEditing too. That didn't help.
import UIKit
import os.log
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var Par1: UITextField!
#IBOutlet var Par2: UITextField!
#IBOutlet var Par3: UITextField!
#IBOutlet var Par4: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Par1.delegate = self
Par1.tag = 0
Par2.delegate = self
Par2.tag = 1
Par3.delegate = self
Par3.tag = 2
Par4.delegate = self
Par4.tag = 3
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
var nextTag:Int
textField.resignFirstResponder()
if (textField.tag == 3) {
nextTag = 0
} else {
nextTag = textField.tag + 1
}
if let nextResponder = textField.superview?.viewWithTag(nextTag) as? UITextField {
nextResponder.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return true
}
#IBAction func Par1Entered(_ sender: UITextField) {
Par1.text = sender.text
}
#IBAction func Par2Entered(_ sender: UITextField) {
Par2.text = sender.text
}
#IBAction func Par3Entered(_ sender: UITextField) {
Par3.text = sender.text
}
#IBAction func Par4Entered(_ sender: UITextField) {
Par4.text = sender.text
}
}
Make the field tags sequential and the in textFieldShouldReturn do the following:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let nextTag = textField.tag + 1
if let nextResponder = textField.superview?.viewWithTag(nextTag) {
nextResponder.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return false
}
If you want to go to the next field without hitting the Next keyboard button there are a few other ways to achieve this.
I took text field outlet collection and bind six text field over there.
I want to becomeFirstResponder of next text field which is in text field outlet collection.
I gave textfields tag 0 to 5 from storyboard.
see,
Main ViewController:
class ViewController: UIViewController {
#IBOutlet var txtSignUp: [UITextField]!
var arrayPlaceHolder:NSArray!
override func viewDidLoad() {
super.viewDidLoad()
arrayPlaceHolder = NSArray(array: ["1","2","3","4","5","6"])
self.setTextFieldValue()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func setTextFieldValue(){
for txtField in txtSignUp{
let tagTxt = txtField.tag
txtField.attributedPlaceholder = NSAttributedString(string:arrayPlaceHolder[tagTxt] as! String, attributes:[NSForegroundColorAttributeName:UIColor.black])
if(tagTxt != ((arrayPlaceHolder.count) - 1)){
txtField.returnKeyType = .next
}
txtField.delegate = self
}
}
}
extension ViewController:UITextFieldDelegate{
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
for txt in txtSignUp{
let nextTxt = (textField.tag + 1)
if txt.tag == nextTxt {
txt.becomeFirstResponder()
break
}
}
return true
}
}
Error:
whose view is not in the window hierarchy!
Explanation:
In this code, I am not able to become next text field as becomeFirstResponder.
Can anyone help me to resolve this issue.
On TVos you have to use the textFieldDidEndEditing function because textFieldShouldReturn won't work to set the next responder:
class MyViewController: UIViewController{
#IBOutlet weak var firstTextField: UITextField!
#IBOutlet weak var secondTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
firstTextField.delegate = self
secondTextField.delegate = self
firstTextField.tag = 0
secondTextField.tag = 1
}
}
extension MyViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
if (textField.tag == 0){
secondTextField.becomeFirstResponder()
}
}
}
I'm very new to all of this and I found some code that got me understanding some of this syntax. I'm trying to create a textfield that lets me type in a value that updates the stepper's value. The stepper currently works (updates the uitextfield) but when I change the value in the textfield it doesn't update the stepper's value, so when I click on the stepper, it reverts back to whatever value it was before I typed in a value... Can anyone tell me why the two functions STracksValueDidChange and CTrackValueDidChange have errors?
Here's my code so far:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var STracks: UITextField!
#IBOutlet weak var STracksStepper: UIStepper!
#IBOutlet weak var CTracks: UITextField!
#IBOutlet weak var CTrackStepper: UIStepper!
override func viewDidLoad() {
super.viewDidLoad()
STracksStepper.autorepeat = true
STracksStepper.maximumValue = 100.0
STracksStepper.minimumValue = 2.0
STracksStepper.stepValue = 2.0
print(STracksStepper.value)
STracks.text = "\(Int(STracksStepper.value))"
STracksStepper.addTarget(self, action: "SstepperValueDidChange:", forControlEvents: .ValueChanged)
STracks.addTarget(self, action: "STextValueDidChange:", forControlEvents: .ValueChanged)
CTrackStepper.autorepeat = true
CTrackStepper.maximumValue = 100.0
CTrackStepper.minimumValue = 2.0
CTrackStepper.stepValue = 2.0
print(CTrackStepper.value)
CTracks.text = "\(Int(CTrackStepper.value))"
CTrackStepper.addTarget(self, action: "CstepperValueDidChange:", forControlEvents: .ValueChanged)
CTracks.addTarget(self, action: "CTextValueDidChange:", forControlEvents: .ValueChanged)
}
//Steppers will update UITextFields
func SstepperValueDidChange(stepper: UIStepper) {
let stepperMapping: [UIStepper: UITextField] = [STracksStepper: STracks]
stepperMapping[stepper]!.text = "\(Int(stepper.value))"
}
func STracksValueDidChange(SText: UITextField) {
let STextMapping: [UITextField: UIStepper] = [STracks: STracksStepper]
STextMapping[SText]!.value = "(SText.text)"
}
func CstepperValueDidChange(stepper: UIStepper) {
let stepperMapping: [UIStepper: UITextField] = [CTrackStepper: CTracks]
stepperMapping[stepper]!.text = "\(Int(stepper.value))"
}
func CTrackValueDidChange(CText: UITextField) {
let CTextMapping: [UITextField: UIStepper] = [CTracks: CTrackStepper]
CTextMapping[CText]!.value = "(CText.text)"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Try something like this.
CTrackStepper.value = Double(Textfield.text)
I am not so sure what the mapping is in your code.
But i don't think you need it for changing the value.
Update, made a project my self:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textfield: UITextField!
#IBOutlet weak var stepper: UIStepper!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func stepperValueChanged(sender: UIStepper) {
textfield.text = String(sender.value)
}
#IBAction func valueChanged(sender: UITextField) {
if Double(sender.text!) != nil {
stepper.value = Double(sender.text!)!
}
}
}
For steppervaluechanged and valuechanged just drag from uistepper and textfield and choose action and change the Anyobject to Uistepper of Uitextfield.
Good luck :)
My ViewController
/ MARK: Properties
#IBOutlet weak var textInput: UITextField!
#IBOutlet weak var labelTop: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
textInput.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(textField: UITextField) {
if (textInput != nil){
labelTop.text = "Searching for \(textField.text)"
textInput.enabled = false
}
}
When I press return on the textfield the code
labelTop.text = "Searching for \(textField.text)"
is called. However the text of labelTop looks like:
Searching for Optional("the text")
I looked at optionals (most times they use ? instead of ! right?) but do not understand how I should get the value without the surrounding 'Optional("")'
You need to unwrap the optional value.
if let text = textInput?.text {
labelTop.text = text
textInput.enabled = false
}