ios 仅当所有文本字段都已填写时才在 Swift 中启用按钮
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34941069/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Enable a button in Swift only if all text fields have been filled out
提问by Derek Mei
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.
我目前有三个 UITextFields 和一个 UIButtonItem。习惯名称字段和目标字段都是手动文本字段,频率字段是选择器视图。
@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.
我尝试更改代码,以便将其他两个字段作为参数,并仅在填写所有三个字段时才启用 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
}
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
}
回答by Leo Dabus
Xcode 9 ? Swift 4
Xcode 9 ? 斯威夫特 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:
您可以addTarget
在文本字段中监视控件事件.editingChanged
并为所有这些字段使用单个选择器方法:
override func viewDidLoad() {
super.viewDidLoad()
doneBarButton.isEnabled = false
[habitNameField, goalField, frequencyField].forEach({ @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
}
.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:
创建选择器方法并guard
结合使用withwhere
子句(Swift 3/4 使用逗号)以确保所有文本字段都不为空,否则就返回。Swift 3 不需要 @objc,但 Swift 4 需要:
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)
}
回答by A.Kant
Swift 5.1 /Xcode 11
斯威夫特 5.1/Xcode 11
@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
}
and then create the selector methodand use guard:
然后创建选择器方法并使用guard:
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
}
}
}
回答by Mayur
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.
最好的方法是在 ViewDidLoad 方法中添加观察者。不仅仅是检查 textField Delegate 方法是否所有 TextField 都已填充。一旦它填满调用 oberserver 方法 & 你只需要启用按钮。
Note:
笔记:
- You can use observer for both enable or disable button
- 您可以将观察者用于启用或禁用按钮
Hope it will help you.
希望它会帮助你。
回答by Stus
I went ahead and abstracted this out a bit into a helper class that one can use for their swift project.
我继续将其抽象为一个可以用于他们的 swift 项目的帮助类。
buttonHelper = ButtonValidationHelper(textFields: [textfield1, textfield2, textfield3, textfield4], buttons: [button])
And then in your View Controller just simply init the helper with
然后在您的视图控制器中只需简单地初始化助手
var buttonHelper: ButtonValidationHelper!
Make sure you keep a strong reference at top to prevent deallocation
确保在顶部保留强引用以防止重新分配
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
回答by Ramy Kfoury
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)
}
}
}
回答by Russell
why not move the checking functionality to a separate function
为什么不将检查功能移至单独的功能
func textFieldShouldReturn(textField: UITextField) -> Bool
{
textField.resignFirstResponder()
setDoneButtonStatus()
}
and then use
然后使用
func textFieldDidEndEditing(textField: UITextField) {
if txtField1.hasText() && textField2.hasText() && textField3.hasText(){
doneBarButton.enabled = true
}
}
回答by Alakh
This works for me: Hope it helps
这对我有用:希望它有帮助
doneBarButton.isEnabled = !textFields.flatMap { 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
.text?.isEmpty }.contains(true)
回答by nslllava
You can create an array of text fields [UITextField]
or an outlet collection. Let's call the array textFields
or something like that.
您可以创建文本字段数组[UITextField]
或插座集合。让我们调用数组textFields
或类似的东西。
extension UITextField {
var textPublisher: AnyPublisher<String, Never> {
NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: self)
.compactMap { private var readyToLogIn: AnyPublisher<Bool, Never> {
return Publishers
.CombineLatest(
emailTextField.textPublisher, passwordTextField.textPublisher
)
.map { email, password in
!email.isEmpty && !password.isEmpty
}
.eraseToAnyPublisher()
}
.object as? UITextField }
.map { readyToLogIn
.receive(on: RunLoop.main)
.assign(to: \.isEnabled, on: signInButton)
.text ?? "" }
.eraseToAnyPublisher()
}
}
And call the code above in a method that monitors text field's text changes.
并在监视文本字段文本更改的方法中调用上面的代码。
回答by user550088
Xcode 10.2 ? Swift 4.3 version of Leo Dabus above.
Xcode 10.2?Swift 4.3 版本的 Leo Dabus 以上。
This solution is for adding a user, probably the most common implementation for such validation.
此解决方案用于添加用户,可能是此类验证最常见的实现。
##代码##}
}
回答by Tiago Almeida
With Combine (Xcode11+, iOS13+) this became easier.
使用Combine(Xcode11+,iOS13+),这变得更容易了。
First you need to be able to create a publisher for text changes:
首先,您需要能够为文本更改创建发布者:
##代码##Then you can combine multiple publishers from multiple text fields:
然后,您可以组合来自多个文本字段的多个发布者:
##代码##And then change your button isEnabled property based on that combined Publisher
然后根据该组合发布者更改您的按钮 isEnabled 属性
##代码##