You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中为App引导页TextField输入构建验证器的合适设计模式?

嗨,这个问题我之前做引导页的时候也碰到过!在Swift里实现灵活且不冗余的TextField验证,策略模式或者基于协议的验证器方案绝对是最优解,既能轻松扩展不同规则,又能避免在代理方法里写一堆乱糟糟的if-else。下面我给你一步步拆解具体实现:

一、先定义验证协议和基础验证器

首先我们抽象出一个验证协议,让所有验证规则都遵循这个协议,这样后续新增规则只需要实现协议即可,完全不影响原有代码。

// 定义验证协议,统一验证方法和返回结果
protocol TextFieldValidator {
    func validate(_ text: String?) -> ValidationResult
}

// 封装验证结果,包含成功状态和错误提示信息
enum ValidationResult {
    case success
    case failure(message: String)
}

// 邮箱验证器:实现具体的邮箱格式校验逻辑
struct EmailValidator: TextFieldValidator {
    func validate(_ text: String?) -> ValidationResult {
        guard let email = text, !email.isEmpty else {
            return .failure(message: "请输入邮箱地址")
        }
        // 这里用了通用的邮箱正则,你可以根据需求调整
        let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
        let emailPredicate = NSPredicate(format:"SELF MATCHES %@", emailRegex)
        return emailPredicate.evaluate(with: email) ? .success : .failure(message: "请输入有效的邮箱格式")
    }
}

// 出生日期验证器:实现日期格式+合理性校验
struct DateOfBirthValidator: TextFieldValidator {
    func validate(_ text: String?) -> ValidationResult {
        guard let dobText = text, !dobText.isEmpty else {
            return .failure(message: "请输入出生日期")
        }
        // 假设用户输入的是yyyy-MM-dd格式,你可以根据实际输入习惯调整格式
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        
        guard let dobDate = dateFormatter.date(from: dobText) else {
            return .failure(message: "请输入正确的日期格式(yyyy-MM-dd)")
        }
        // 额外校验:出生日期不能是未来日期
        if dobDate > Date() {
            return .failure(message: "出生日期不能晚于当前日期")
        }
        return .success
    }
}
二、给TextField绑定专属验证器

我们可以通过关联对象给UITextField扩展一个validator属性,这样每个TextField可以直接绑定对应的验证器,不用在代理方法里判断是哪个TextField。

import ObjectiveC

// 用关联对象给UITextField添加验证器属性
private var ValidatorAssociationKey: UInt8 = 0

extension UITextField {
    var validator: TextFieldValidator? {
        get {
            return objc_getAssociatedObject(self, &ValidatorAssociationKey) as? TextFieldValidator
        }
        set {
            objc_setAssociatedObject(self, &ValidatorAssociationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}
三、在ViewController中统一处理验证

现在在引导页的ViewController里,只需要给每个TextField绑定对应验证器,然后在textFieldDidEndEditing里统一调用验证逻辑即可:

class OnboardingVC: UIViewController, UITextFieldDelegate {
    
    @IBOutlet weak var emailTextField: UITextField!
    @IBOutlet weak var dobTextField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 给每个TextField绑定对应的验证器
        emailTextField.validator = EmailValidator()
        dobTextField.validator = DateOfBirthValidator()
        
        // 设置代理
        emailTextField.delegate = self
        dobTextField.delegate = self
    }
    
    func textFieldDidEndEditing(_ textField: UITextField) {
        // 取出当前TextField的验证器,执行验证
        guard let validator = textField.validator else { return }
        let result = validator.validate(textField.text)
        
        // 根据验证结果给用户反馈
        switch result {
        case .success:
            // 验证通过:恢复正常样式,清空错误提示
            textField.layer.borderColor = UIColor.systemGray.cgColor
            textField.layer.borderWidth = 1.0
            // 如果有错误提示标签,这里可以隐藏
        case .failure(let message):
            // 验证失败:显示错误样式+提示用户
            textField.layer.borderColor = UIColor.systemRed.cgColor
            textField.layer.borderWidth = 1.0
            let alert = UIAlertController(title: "输入有误", message: message, preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "好的", style: .default))
            present(alert, animated: true)
        }
    }
}
四、后续扩展优化

如果之后需要新增其他验证规则(比如手机号、密码强度),只需要新建一个遵循TextFieldValidator的结构体即可,完全不用修改原有代码,完美符合开闭原则。

另外你还可以把错误反馈的逻辑封装成TextField的扩展,让样式更统一:

extension UITextField {
    func applyValidationStyle(_ result: ValidationResult) {
        switch result {
        case .success:
            self.layer.borderColor = UIColor.systemGray.cgColor
            self.layer.borderWidth = 1.0
            self.rightView = nil
        case .failure:
            self.layer.borderColor = UIColor.systemRed.cgColor
            self.layer.borderWidth = 1.0
            // 给TextField右侧加个错误图标
            let errorIcon = UIImageView(image: UIImage(systemName: "exclamationmark.triangle"))
            errorIcon.tintColor = .systemRed
            errorIcon.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
            self.rightView = errorIcon
            self.rightViewMode = .always
        }
    }
}

然后在textFieldDidEndEditing里直接调用textField.applyValidationStyle(result)就可以了,代码更简洁。

内容的提问来源于stack exchange,提问作者D.Rees

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:21:23