Enable a button in Swift only if all text fields have been filled out

前端 未结 10 611
误落风尘
误落风尘 2020-12-01 01:20

I am having trouble figuring out how to change my code to make it so the Done button in the navigation bar is enabled when my three text fields are filled out.

I cur

相关标签:
10条回答
  • 2020-12-01 01:25

    Xcode 10.2 • Swift 4.3 version of Leo Dabus above.

    This solution is for adding a user, probably the most common implementation for such validation.

    override func viewDidLoad() {
    super.viewDidLoad()
    addUserButton.backgroundColor = disabledButtonColor
    addUserButton.isEnabled = false
    
    [emailField, userNameField, firstNameField, lastNameField].forEach { (field) in
        field?.addTarget(self,
                         action: #selector(editingChanged(_:)),
                         for: .editingChanged)
    }}
    
    
    
    @objc private func editingChanged(_ textField: UITextField) {
    if textField.text?.count == 1 {
        if textField.text?.first == " " {
            textField.text = ""
            return
        }
    }
    guard
        let email       = emailField.text,      !email.isEmpty,
        let userName    = userNameField.text,   !userName.isEmpty,
        let firstName   = firstNameField.text,  !firstName.isEmpty,
        let lastName    = lastNameField.text,   !lastName.isEmpty
        else {
            addUserButton.isEnabled = false
            addUserButton.backgroundColor = disabledButtonColor
            return
    }
    addUserButton.isEnabled = true
    addUserButton.backgroundColor = activeButtonColor
    

    }

    0 讨论(0)
  • 2020-12-01 01:27

    I went ahead and abstracted this out a bit into a helper class that one can use for their swift project.

    import Foundation
    import UIKit
    
    class ButtonValidationHelper {
    
      var textFields: [UITextField]!
      var buttons: [UIButton]!
    
      init(textFields: [UITextField], buttons: [UIButton]) {
    
        self.textFields = textFields
        self.buttons = buttons
    
        attachTargetsToTextFields()
        disableButtons()
        checkForEmptyFields()
      }
    
      //Attach editing changed listeners to all textfields passed in
      private func attachTargetsToTextFields() {
        for textfield in textFields{
            textfield.addTarget(self, action: #selector(textFieldsIsNotEmpty), for: .editingChanged)
        }
      }
    
      @objc private func textFieldsIsNotEmpty(sender: UITextField) {
        sender.text = sender.text?.trimmingCharacters(in: .whitespaces)
        checkForEmptyFields()
      }
    
    
      //Returns true if the field is empty, false if it not
      private func checkForEmptyFields() {
    
        for textField in textFields{
            guard let textFieldVar = textField.text, !textFieldVar.isEmpty else {
                disableButtons()
                return
            }
        }
        enableButtons()
      }
    
      private func enableButtons() {
        for button in buttons{
            button.isEnabled = true
        }
      }
    
      private func disableButtons() {
        for button in buttons{
            button.isEnabled = false
        }
      }
    
    }
    

    And then in your View Controller just simply init the helper with

    buttonHelper = ButtonValidationHelper(textFields: [textfield1, textfield2, textfield3, textfield4], buttons: [button])
    

    Make sure you keep a strong reference at top to prevent deallocation

    var buttonHelper: ButtonValidationHelper!
    
    0 讨论(0)
  • 2020-12-01 01:28

    Xcode 9 • Swift 4

    You can addTarget to your text fields to monitor for the control event .editingChanged and use a single selector method for all of them:

    override func viewDidLoad() {
        super.viewDidLoad()
        doneBarButton.isEnabled = false
        [habitNameField, goalField, frequencyField].forEach({ $0.addTarget(self, action: #selector(editingChanged), for: .editingChanged) })
    }
    

    Create the selector method and use guard combined with where clause (Swift 3/4 uses a comma) to make sure all text fields are not empty otherwise just return. Swift 3 does not require @objc, but Swift 4 does:

    @objc func editingChanged(_ textField: UITextField) {
        if textField.text?.characters.count == 1 {
            if textField.text?.characters.first == " " {
                textField.text = ""
                return
            }
        }
        guard
            let habit = habitNameField.text, !habit.isEmpty,
            let goal = goalField.text, !goal.isEmpty,
            let frequency = frequencyField.text, !frequency.isEmpty
        else {
            doneBarButton.isEnabled = false
            return
        }
        doneBarButton.isEnabled = true
    }
    

    sample

    0 讨论(0)
  • 2020-12-01 01:33

    With Combine (Xcode11+, iOS13+) this became easier.

    First you need to be able to create a publisher for text changes:

    extension UITextField {
    
        var textPublisher: AnyPublisher<String, Never> {
            NotificationCenter.default
                .publisher(for: UITextField.textDidChangeNotification, object: self)
                .compactMap { $0.object as? UITextField }
                .map { $0.text ?? "" }
                .eraseToAnyPublisher()
        }
    
    }
    

    Then you can combine multiple publishers from multiple text fields:

    private var readyToLogIn: AnyPublisher<Bool, Never> {
       return Publishers
          .CombineLatest(
             emailTextField.textPublisher, passwordTextField.textPublisher
          )
          .map { email, password in
             !email.isEmpty && !password.isEmpty
          }
          .eraseToAnyPublisher()
    }
    
    

    And then change your button isEnabled property based on that combined Publisher

    readyToLogIn
       .receive(on: RunLoop.main)
       .assign(to: \.isEnabled, on: signInButton)
    
    0 讨论(0)
  • 2020-12-01 01:34

    You can create an array of text fields [UITextField] or an outlet collection. Let's call the array textFields or something like that.

    doneBarButton.isEnabled = !textFields.flatMap { $0.text?.isEmpty }.contains(true)
    

    And call the code above in a method that monitors text field's text changes.

    0 讨论(0)
  • 2020-12-01 01:35
    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        if (textField == self.textField1) { /* do Something */ }
        else if (textField == self.textField2) { /* do Something */ }
        else if (textField == self.textField3) { /* do Something */ }
    
        // regardless of what you do before, doneBarButton is enabled when all are not empty
        doneBarButton.enabled = (textField1.length != 0) && (textField2.length != 0) && (textField3.length != 0)
        return true
    

    }

    0 讨论(0)
提交回复
热议问题