Enable a button in Swift only if all text fields h

2019-01-17 01:57发布

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 currently have three UITextFields and one UIButtonItem. Both the habitNameField and the goalField are manual text fields, and the frequencyField is a Picker View.

@IBOutlet weak var habitNameField: UITextField!
@IBOutlet weak var goalField: UITextField!
@IBOutlet weak var frequencyField: UITextField!

@IBOutlet weak var doneBarButton: UIBarButtonItem!

I also have the following function that works when there is something typed in the first field.

func textField(habitNameField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let oldHabitNameText: NSString = habitNameField.text!
    let newHabitNameText: NSString = oldHabitNameText.stringByReplacingCharactersInRange(range, withString: string)
    doneBarButton.enabled = (newHabitNameText.length != 0)
    return true
}

I tried change the code so that it took in the other two fields as parameters and enabled the doneBarButton only if all three fields were filled out.

func textField(habitNameField: UITextField, goalField: UITextField, frequencyField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let habitNameText: NSString = (habitNameField.text!).stringByReplacingCharactersInRange(range, withString: string)
    let goalText: NSString = (goalField.text!).stringByReplacingCharactersInRange(range, withString: string)
    let frequencyText: NSString = (frequencyField.text!).stringByReplacingCharactersInRange(range, withString: string)

    doneBarButton.enabled = (habitNameText.length != 0) && (goalText.length != 0) && (frequencyText.length != 0)
    return true
}

However, it's not working, even when I fill out all three text fields.

I would really appreciate any help, and thanks to anyone who contributes in advance!

All code here:

class HabitDetailViewController: UITableViewController, UITextFieldDelegate, UIPickerViewDataSource,UIPickerViewDelegate {
@IBOutlet weak var habitNameField: UITextField!
@IBOutlet weak var goalField: UITextField!
@IBOutlet weak var doneBarButton: UIBarButtonItem!
@IBOutlet weak var frequencyField: UITextField!

var frequencies = ["Day", "Week", "Month", "Year"]
var frequencyPicker = UIPickerView()

var habitToEdit: HabitItem?
weak var delegate: HabitDetailViewControllerDelegate?

@IBAction func cancel() {
    delegate?.habitDetailViewControllerDidCancel(self)
}

@IBAction func done() {
    print("You plan to do \(habitNameField.text!) \(goalField.text!) times a \(frequencyField.text!.lowercaseString).")
    if let habit = habitToEdit {
        habit.name = habitNameField.text!
        habit.numberLeft = Int(goalField.text!)!
        habit.frequency = frequencyField.text!
        delegate?.habitDetailViewController(self, didFinishEditingHabit: habit)
    } else {
        let habit = HabitItem()
        habit.name = habitNameField.text!
        habit.numberLeft = Int(goalField.text!)!
        habit.frequency = frequencyField.text!
        habit.completed = false
        delegate?.habitDetailViewController(self, didFinishAddingHabit: habit)
    }
}

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    habitNameField.becomeFirstResponder()
    frequencyPicker.hidden = false
}

override func viewDidLoad() {
    super.viewDidLoad()
    frequencyPicker.dataSource = self
    frequencyPicker.delegate = self
    doneBarButton.enabled = false
    habitNameField.addTarget(self, action: "checkFields:", forControlEvents: .EditingChanged)
    goalField.addTarget(self, action: "checkFields:", forControlEvents: .EditingChanged)
    frequencyField.addTarget(self, action: "checkFields:", forControlEvents: .EditingChanged)
    frequencyField.inputView = frequencyPicker
    if let habit = habitToEdit {
        title = "Edit Item"
        habitNameField.text = habit.name
        goalField.text = String(habit.numberLeft)
        doneBarButton.enabled = true
    }
}

override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
    return nil
}

func textField(habitNameField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let oldHabitNameText: NSString = habitNameField.text!
    let newHabitNameText: NSString = oldHabitNameText.stringByReplacingCharactersInRange(range, withString: string)
    doneBarButton.enabled = (newHabitNameText.length != 0)
    return true
}

func checkFields(sender: UITextField) {
    sender.text = sender.text?.stringByTrimmingCharactersInSet(.whitespaceCharacterSet())
    guard
        let habit = habitNameField.text where !habit.isEmpty,
        let goal = goalField.text where !goal.isEmpty,
        let frequency = frequencyField.text where !frequency.isEmpty
        else { return }
    // enable your button if all conditions are met
    doneBarButton.enabled = true
}

8条回答
SAY GOODBYE
2楼-- · 2019-01-17 02:12
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

}

查看更多
闹够了就滚
3楼-- · 2019-01-17 02:16

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!
查看更多
虎瘦雄心在
4楼-- · 2019-01-17 02:22

The best way would be to Add observer in ViewDidLoad method. Than just check in textField Delegate method whether all TextFields are filled up or not. Once its filled up call oberserver method & in that you just need to enable button.

Note:

  • You can use observer for both enable or disable button

Hope it will help you.

查看更多
▲ chillily
5楼-- · 2019-01-17 02:27

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.

查看更多
叼着烟拽天下
6楼-- · 2019-01-17 02:30

why not move the checking functionality to a separate function

func setDoneButtonStatus()
{
    let habitNameText: NSString = (habitNameField.text!).stringByReplacingCharactersInRange(range, withString: string)
    let goalText: NSString = (goalField.text!).stringByReplacingCharactersInRange(range, withString: string)
    let frequencyText: NSString = (frequencyField.text!).stringByReplacingCharactersInRange(range, withString: string)

    doneBarButton.enabled = (habitNameText.length != 0) && (goalText.length != 0) && (frequencyText.length != 0)
}

and then use

func textFieldShouldReturn(textField: UITextField) -> Bool
{
    textField.resignFirstResponder()
    setDoneButtonStatus()
}
查看更多
爷、活的狠高调
7楼-- · 2019-01-17 02:35

This works for me: Hope it helps

  func textFieldDidEndEditing(textField: UITextField) {
        if txtField1.hasText() && textField2.hasText() && textField3.hasText(){
            doneBarButton.enabled = true
        }
    }
查看更多
登录 后发表回答