Включить кнопку в Swift только в том случае, если все текстовые поля заполнены

Мне трудно понять, как изменить свой код, чтобы сделать его так, чтобы кнопка "Готово" на панели навигации включалась, когда заполнялись мои три текстовых поля.

В настоящее время у меня есть три UITextFields и один UIButtonItem. И habitNameField, и targetField - это текстовые поля вручную, а FrequencyField - это представление Picker.

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

Ответ 1

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
}

образец

Ответ 2

Swift 3/Xcode 8.2

введите описание изображения здесь

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

а затем создайте селекторный метод и используйте guard:

@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. В быстрых 3 ", где" == → > ","

Ответ 3

Лучший способ - добавить наблюдателя в метод ViewDidLoad. Вместо того, чтобы просто проверять метод textField Delegate, заполняется ли все текстовые поля или нет. После того, как он заполнил метод oberserver вызова, и вам просто нужно включить кнопку.

Примечание:

  • Вы можете использовать наблюдателя для кнопки включения или отключения

Надеюсь, это поможет вам.

Ответ 4

Я пошел вперед и немного отвлек его на вспомогательный класс, который можно использовать для их быстрого проекта.

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

}

И затем в вашем контроллере просмотра просто запустите помощник с помощью

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

Удостоверьтесь, что вы держите сильную ссылку сверху, чтобы предотвратить освобождение

var buttonHelper: ButtonValidationHelper!

Ответ 5

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

}

Ответ 6

почему бы не переместить функциональность проверки на отдельную функцию

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

Ответ 7

Это работает для меня: надеюсь, что это поможет

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

Ответ 8

Вы можете создать массив текстовых полей [UITextField] или коллекцию выходных файлов. Позвольте называть массив textFields или что-то в этом роде.

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

И вызовите код выше в методе, который отслеживает изменение текста текстового поля.

Ответ 9

Xcode 10.2 • Swift 4.3 version of Leo Dabus above.

Это решение для добавления пользователя, вероятно, самая распространенная реализация для такой проверки.

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

}

Ответ 10

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

А затем измените свойство кнопки isEnabled на основе этого объединенного издателя

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