ios Swift 中的表单验证
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36303653/
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
Form validation in Swift
提问by éliette
I have recently commenced working on a Swift (2) iOS project, and started to face circumstances where I have forms with many fields which need validating.
我最近开始在 Swift (2) iOS 项目上工作,并开始面临这样的情况:我的表单包含许多需要验证的字段。
I have a predominantly .Net background, and with Binding and Annotations it is possible to implement form validation cleanly in a low maintenance manner, on large forms, with few lines of code.
我有一个主要的 .Net 背景,并且通过绑定和注释,可以以低维护的方式在大型表单上干净地实现表单验证,只需几行代码。
Since embracing Swift, I have come across many examples detailing validation in various fashions, but everything I have encountered thus far seems very labour intensive and high maintenance, or the explanations focus on conducting the validation itself rather than connecting the validation process between the model and the view efficiently.
自从拥抱 Swift 以来,我遇到了许多以各种方式详细说明验证的示例,但到目前为止我遇到的所有事情似乎都非常劳动密集且维护成本高,或者解释侧重于进行验证本身,而不是将模型和模型之间的验证过程连接起来视图有效。
For example:
例如:
My current solution defines extensions which can be checked in a function, rule-by-rule, when validating field input, however I feel there must be a more scalable solution to this problem.
我当前的解决方案定义了在验证字段输入时可以在函数中逐条检查的扩展,但是我觉得必须有一个更具可扩展性的解决方案来解决这个问题。
What approaches can be taken, when validating forms with potentially many inputs, that promote maintainability?
在验证具有潜在许多输入的表单时,可以采取哪些方法来提高可维护性?
For example sake, we could discuss a hypothetical form with:
例如,我们可以讨论一个假设形式:
- A textField requesting a number be entered (Int).
- A textField requesting a decimal number be entered (Double).
- A textField requesting a number be entered, matching specific rules (Eg, is prime) (Double).
- A textField requesting a string be entered of some length kL.
- A textField requesting a string be entered conforming to some custom rule.
- 请求输入数字的文本字段 (Int)。
- 请求输入十进制数的文本字段(双)。
- 请求输入数字的文本字段,匹配特定规则(例如,素数)(双)。
- 请求输入长度为 kL 的字符串的 textField。
- 要求输入符合某些自定义规则的字符串的 textField。
Of course, I am not looking for an implementation that literally satisfies the list above, but instead a method or approach that is scaleable across these types of scenarios.
当然,我不是在寻找真正满足上面列表的实现,而是寻找一种可在这些类型的场景中进行扩展的方法或方法。
回答by avismara
"Ugh, forms"
“呃,表格”
-Sir Albert Einstein
——阿尔伯特·爱因斯坦爵士
Yes, building a scaleable form in iOS can be a difficult and monotonous job. Which is why I have a base class called a FormViewController
that exposes a few common validation methods and a few methods that you can use to add customised validation.
是的,在 iOS 中构建可缩放的表单可能是一项困难而单调的工作。这就是为什么我有一个名为 a 的基类FormViewController
,它公开了一些常见的验证方法和一些可用于添加自定义验证的方法。
Now, the following code could be very long, and I am not going to explain each line. Do revert in the form of comments, if you have any doubts.
现在,下面的代码可能很长,我不打算解释每一行。如果您有任何疑问,请以评论的形式回复。
import UIKit
typealias TextFieldPredicate = ( (String) -> (Bool) )
class FormViewController : UIViewController {
var activeTextField : UITextField!
private var mandatoryFields = [UITextField]()
private var emptyErrorMessages = [String]()
private var emailFields = [UITextField]()
private var emailErrorMessages = [String]()
private var specialValidationFields = [UITextField]()
private var specialValidationMethods = [TextFieldPredicate]()
private var specialValidationErrorMessages = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
registerForNotifications()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func registerForNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(FormViewController.keyboardWillShow(_:)), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(FormViewController.keyboardWillHide(_:)), name:UIKeyboardWillHideNotification, object: nil);
}
func keyboardWillShow(notification:NSNotification?) {
let keyboardSize = notification?.userInfo![UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
self.view.frame.origin.y = 0
let keyboardYPosition = self.view.frame.size.height - keyboardSize!.height
if keyboardYPosition < self.activeTextField!.frame.origin.y {
UIView.animateWithDuration(GlobalConstants.AnimationTimes.SHORT) { () -> Void in
self.view.frame.origin.y = self.view.frame.origin.y - keyboardSize!.height + 30
}
}
}
func keyboardWillHide(notification:NSNotification?) {
UIView.animateWithDuration(GlobalConstants.AnimationTimes.SHORT) { () -> Void in
self.view.frame.origin.y = 0
}
}
func validateEmailForFields(emailTextFields:[UITextField]) -> [Bool] {
var validatedBits = [Bool]()
for emailTextField in emailTextFields {
if let text = emailTextField.text where !text.isValidEmail() {
emailTextField.shakeViewForTimes(WelcomeViewController.ERROR_SHAKE_COUNT)
validatedBits.append(false)
} else {
validatedBits.append(true)
}
}
return validatedBits
}
func validateSpecialTextFields(specialTextFields:[UITextField]) -> [Bool] {
var validatedBits = [Bool]()
for specialTextField in specialTextFields {
let specialValidationMethod = self.specialValidationMethods[ specialValidationFields.indexOf(specialTextField)!]
validatedBits.append(specialValidationMethod(specialTextField.text!))
}
return validatedBits
}
func validateEmptyFields(textFields : [UITextField]) -> [Bool] {
var validatedBits = [Bool]()
for textField in textFields {
if let text = textField.text where text.isEmpty {
textField.shakeViewForTimes(WelcomeViewController.ERROR_SHAKE_COUNT)
validatedBits.append(false)
} else {
validatedBits.append(true)
}
}
return validatedBits
}
func addMandatoryField(textField : UITextField, message : String) {
self.mandatoryFields.append(textField)
self.emptyErrorMessages.append(message)
}
func addEmailField(textField : UITextField , message : String) {
textField.keyboardType = .EmailAddress
self.emailFields.append(textField)
self.emailErrorMessages.append(message)
}
func addSpecialValidationField(textField : UITextField , message : String, textFieldPredicate : TextFieldPredicate) {
self.specialValidationErrorMessages.append(message)
self.specialValidationMethods.append(textFieldPredicate)
self.specialValidationFields.append(textField)
}
func errorMessageForEmptyTextField(textField : UITextField) throws -> String {
if self.mandatoryFields.contains(textField) {
return self.emptyErrorMessages[self.mandatoryFields.indexOf(textField)!]
} else {
throw ValidationError.NonMandatoryTextField
}
}
func errorMessageForMultipleEmptyErrors() -> String {
return "Fields cannot be empty"
}
func errorMessageForMutipleEmailError() -> String {
return "Invalid email addresses"
}
@IBAction func didTapFinishButton(sender:AnyObject?) {
if let errorMessage = self.errorMessageAfterPerformingValidation() {
self.showVisualFeedbackWithErrorMessage(errorMessage)
return
}
self.didCompleteValidationSuccessfully()
}
func showVisualFeedbackWithErrorMessage(errorMessage : String) {
fatalError("Implement this method")
}
func didCompleteValidationSuccessfully() {
}
func errorMessageAfterPerformingValidation() -> String? {
if let errorMessage = self.errorMessageAfterPerformingEmptyValidations() {
return errorMessage
}
if let errorMessage = self.errorMessageAfterPerformingEmailValidations() {
return errorMessage
}
if let errorMessage = self.errorMessageAfterPerformingSpecialValidations() {
return errorMessage
}
return nil
}
private func errorMessageAfterPerformingEmptyValidations() -> String? {
let emptyValidationBits = self.performEmptyValidations()
var index = 0
var errorCount = 0
var errorMessage : String?
for validation in emptyValidationBits {
if !validation {
errorMessage = self.emptyErrorMessages[index]
errorCount += 1
}
if errorCount > 1 {
return self.errorMessageForMultipleEmptyErrors()
}
index = index + 1
}
return errorMessage
}
private func errorMessageAfterPerformingEmailValidations() -> String? {
let emptyValidationBits = self.performEmailValidations()
var index = 0
var errorCount = 0
var errorMessage : String?
for validation in emptyValidationBits {
if !validation {
errorMessage = self.emailErrorMessages[index]
errorCount += 1
}
if errorCount > 1 {
return self.errorMessageForMutipleEmailError()
}
index = index + 1
}
return errorMessage
}
private func errorMessageAfterPerformingSpecialValidations() -> String? {
let emptyValidationBits = self.performSpecialValidations()
var index = 0
for validation in emptyValidationBits {
if !validation {
return self.specialValidationErrorMessages[index]
}
index = index + 1
}
return nil
}
func performEqualValidationsForTextField(textField : UITextField, anotherTextField : UITextField) -> Bool {
return textField.text! == anotherTextField.text!
}
private func performEmptyValidations() -> [Bool] {
return validateEmptyFields(self.mandatoryFields)
}
private func performEmailValidations() -> [Bool] {
return validateEmailForFields(self.emailFields)
}
private func performSpecialValidations() -> [Bool] {
return validateSpecialTextFields(self.specialValidationFields)
}
}
extension FormViewController : UITextFieldDelegate {
func textFieldDidBeginEditing(textField: UITextField) {
self.activeTextField = textField
}
func textFieldDidEndEditing(textField: UITextField) {
self.activeTextField = nil
}
}
enum ValidationError : ErrorType {
case NonMandatoryTextField
}
回答by sww314
Another option that seems good so far is SwiftValidator. It is an active project and only took a few minutes for me to setup in my project.
另一个目前看起来不错的选择是 SwiftValidator。这是一个活跃的项目,我只花了几分钟就在我的项目中进行了设置。
回答by Suran
Even though it is 3 years too late, I'm putting this in here for anyone who wants a flexible and extendible option. As far as I can tell, it will be an exact fit as an answer for the question.
即使已经晚了 3 年,我还是将它放在此处供任何想要灵活和可扩展选项的人使用。据我所知,它非常适合作为问题的答案。
Please have a look at ATGValidator.
请查看ATGValidator。
I'm not adding any code here since the readme in the framework link has extensive documentation for the same.
我没有在这里添加任何代码,因为框架链接中的自述文件有大量相同的文档。
回答by Jarod
There's EGFormValidatorlibrary written in Swift 3. It's flexible and easy to use.
有用Swift 3 编写的EGFormValidator库。它灵活且易于使用。