仅当所有文本字段都已填写时才在 Swift 中启用按钮
Posted
技术标签:
【中文标题】仅当所有文本字段都已填写时才在 Swift 中启用按钮【英文标题】:Enable a button in Swift only if all text fields have been filled out 【发布时间】:2016-01-22 07:17:44 【问题描述】:我无法弄清楚如何更改我的代码,以便在填写我的三个文本字段时启用导航栏中的“完成”按钮。
我目前有三个 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
【问题讨论】:
【参考方案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
sample
【讨论】:
这是有效的,但出于某种原因,即使只填写了部分字段,它也有效。这是为什么呢? 杜尔。这是我的菜鸟错误。谢谢你帮我抓住了。此外,频率的最终文本字段不起作用,因为它是一个选择器视图。有什么想法吗? 您可以创建一个布尔变量来监控它,并在用户设置它后对其进行标记。然后只需将其添加到保护语句中 如何检查电子邮件 id 并且单个文本字段不为空,尝试过但不工作 let emaiTextField = existingEmailAddressTextField.text, !emaiTextField.isEmpty, let validEmail = existingEmailAddressTextField.text, self.validateEmail(validEmail) ==真, 如何检查相同文本字段的有效电子邮件 ID【参考方案2】:Swift 5.1 /Xcode 11
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
【讨论】:
应该是正确的答案,对我来说效果很好,委托方法不提供类似 .editingChanged 的东西 很好的解决方案!您需要将“textFieldsIsNotEmpty”标记为“@objc”,否则编译器会抛出错误。请更新答案。 如何检查是否有效的电子邮件 ID 并为相同的文本字段留空【参考方案3】:最好的方法是在 ViewDidLoad 方法中添加观察者。不仅仅是检查 textField Delegate 方法是否所有 TextField 都已填满。一旦它填满调用 oberserver 方法,您只需要启用按钮。
注意:
您可以将观察者用于启用或禁用按钮希望对你有所帮助。
【讨论】:
你能举例说明如何添加观察者吗?谢谢!【参考方案4】:我继续将其抽象为一个帮助类,可以用于他们的 swift 项目。
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
【讨论】:
请注意,这仅适用于 ios(10.0 及更高版本)、tvOS(10.0 及更高版本)。顺便说一句,它不再是一种方法。现在它是一个计算属性【参考方案8】:您可以创建文本字段数组[UITextField]
或插座集合。让我们将数组称为 textFields
或类似的名称。
doneBarButton.isEnabled = !textFields.flatMap $0.text?.isEmpty .contains(true)
并在监控文本字段文本变化的方法中调用上面的代码。
【讨论】:
【参考方案9】:使用 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】: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)
【讨论】:
【参考方案11】:Xcode 10.2 • Swift 4.3 版本的 Leo Dabus 以上。
此解决方案用于添加用户,可能是最常见的实现 进行此类验证。
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
【讨论】:
以上是关于仅当所有文本字段都已填写时才在 Swift 中启用按钮的主要内容,如果未能解决你的问题,请参考以下文章
当所有文本字段都填写在uitableviewcell(SWIFT)中时如何启用按钮
仅当 Validation 为 True 时才启用 Jbutton
仅当前面有 (\d2 -) 时才在 html 中查找链接 (href) [重复]