仅当所有文本字段都已填写时才在Swift中启用按钮

时间:2016-01-22 07:17:44

标签: ios iphone swift uitextfield swift2

我无法弄清楚如何更改我的代码,以便在填写完三个文本字段时启用导航栏中的“完成”按钮。

我目前有三个UITextField和一个UIButtonItem。 habitNameField和goalField都是手动文本字段,frequencyField是Picker View。

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

@IBOutlet weak var doneBarButton: UIBarButtonItem!

我还有以下功能,当第一个字段中输入了某些内容时,它可以正常工作。

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
}

我尝试更改代码,以便将其他两个字段作为参数,并且只有在填写了所有三个字段时才启用doneBarButton。

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
}

然而,即使我填写所有三个文本字段,它也无法正常工作。

我真的很感激任何帮助,感谢任何提前做出贡献的人!

此处的所有代码:

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
}

11 个答案:

答案 0 :(得分:34)

Xcode 9•Swift 4

您可以addTarget到您的文本字段来监控控制事件.editingChanged,并对所有这些字段使用单一选择器方法:

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

创建选择器方法并使用guard结合where子句(Swift 3/4使用逗号)来确保所有文本字段都不为空,否则只返回。 Swift 3不需要@objc,但Swift 4不需要:

@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

答案 1 :(得分:7)

Swift 3 / Xcode 8.2

enter image description here

override func viewDidLoad() {
    super.viewDidLoad()
    setupAddTargetIsNotEmptyTextFields()        
}

func setupAddTargetIsNotEmptyTextFields() {
    okButton.isHidden = true //hidden okButton
    nameUserTextField.addTarget(self, action: #selector(textFieldsIsNotEmpty), 
                                for: .editingChanged)
    emailUserTextField.addTarget(self, action: #selector(textFieldsIsNotEmpty), 
                                for: .editingChanged)
    passwordUserTextField.addTarget(self, action: #selector(textFieldsIsNotEmpty), 
                                for: .editingChanged)
    confimPasswordUserTextField.addTarget(self, action: #selector(textFieldsIsNotEmpty), 
                                for: .editingChanged)        
   }

然后创建选择器方法并使用后卫

@objc func textFieldsIsNotEmpty(sender: UITextField) {

    sender.text = sender.text?.trimmingCharacters(in: .whitespaces)

    guard
      let name = nameUserTextField.text, !name.isEmpty,
      let email = emailUserTextField.text, !email.isEmpty,
      let password = passwordUserTextField.text, !password.isEmpty,
      let confirmPassword = confimPasswordUserTextField.text,
          password == confirmPassword          
      else
    {
      self.okButton.isHidden = true
      return
    }
    // enable okButton if all conditions are met
    okButton.isHidden = false
   }

P.S。在swift 3“其中”==&gt;&gt;&gt; “<强>,

答案 2 :(得分:3)

最好的方法是在ViewDidLoad方法中添加观察者。而不仅仅是检查textField Delegate方法是否所有TextField都被填满。一旦填满了电话oberserver方法&amp;因为你只需要启用按钮。

注意:

  • 您可以将observer用于启用或禁用按钮

希望它会对你有所帮助。

答案 3 :(得分:1)

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

}

答案 4 :(得分:1)

为什么不将检查功能移到单独的功能

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

然后使用

func textFieldShouldReturn(textField: UITextField) -> Bool
{
    textField.resignFirstResponder()
    setDoneButtonStatus()
}

答案 5 :(得分:1)

这对我有用:希望有所帮助

  func textFieldDidEndEditing(textField: UITextField) {
        if txtField1.hasText() && textField2.hasText() && textField3.hasText(){
            doneBarButton.enabled = true
        }
    }

答案 6 :(得分:1)

我继续将这一点抽象出一个可以用于快速项目的辅助类。

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

}

然后在您的View Controller中只需使用

初始化帮助程序
buttonHelper = ButtonValidationHelper(textFields: [textfield1, textfield2, textfield3, textfield4], buttons: [button])

确保在顶部保留强大的引用以防止重新分配

var buttonHelper: ButtonValidationHelper!

答案 7 :(得分:0)

您可以创建文本字段[UITextField]或插座集合的数组。让我们调用数组textFields或类似的东西。

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

并在监视文本字段文本更改的方法中调用上面的代码。

答案 8 :(得分:0)

  

Xcode 10.2•以上是Leo Dabus的Swift 4.3版本。

此解决方案用于添加用户,这可能是最常见的实现 进行此类验证。

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

}

答案 9 :(得分:0)

有了Combine(Xcode11 +,iOS13 +),这变得更加容易。

首先,您需要能够创建用于文本更改的发布者:

extension UITextField {

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

}

然后,您可以合并来自多个文本字段的多个发布者:

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

然后基于该合并的Publisher更改按钮的isEnabled属性

readyToLogIn
   .receive(on: RunLoop.main)
   .assign(to: \.isEnabled, on: signInButton)

答案 10 :(得分:0)

Swift 5.0+

我会做这样的事情,这是已经给出的答案的组合,但更加精简和最新:

在您的viewDidLoad()

    [usernameTextField, passwordTextField].forEach {
        $0?.addTarget(self,
                      action: #selector(editingChanged(_:)),
                      for: .editingChanged)
    }

然后创建以下内容:

@objc func editingChanged(_ textField: UITextField) {
    
    // Trim whitespace and newlines
    textField.text = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines)
    
    // Assumes loginButton exists and there's a reference to it in the current scope.
    loginButton.isEnabled = ![usernameTextField, passwordTextField].compactMap {
        $0.text?.isEmpty
    }.contains(true)
}