Enable/Disable 按钮,用于验证在文本字段中输入的 phone 数字

Enable/Disable button with validating phone number entered in a textfield

作为一个整体,我对 ReactiveSwift 和 MVVM 非常陌生。我正在尝试根据验证结果验证 phone 输入到文本字段中的数字和 enable/disable 按钮。

在应用程序中,有一个文本字段和一个名为“提交”的 UIButton 按钮。对于 phone 号码验证,我使用了一个名为 [PhoneNumberKit][1] 的开源库。它还提供了一个 UITextField 子类,用于格式化用户输入。

我混合了一个看起来像这样的解决方案。

class ViewController: UIViewController {
    @IBOutlet weak var textField: PhoneNumberTextField!
    @IBOutlet weak var submitButton: UIButton!


    override func viewDidLoad() {
        super.viewDidLoad()
        textField.becomeFirstResponder()
        submitButton.isEnabled = false

        let textValuesSignal = textField.reactive.continuousTextValues
        SignalProducer(textValuesSignal).start { result in
            switch result {
            case .value(let value):
                print(value)
                self.submitButton.isEnabled = self.isPhoneNumberValid(value)
            case .failed(let error):
                print(error)
                self.submitButton.isEnabled = false
            case .interrupted:
                print("inturrupted")
                self.submitButton.isEnabled = false
            case .completed:
                print("completed")
            }
        }
    }

    func isPhoneNumberValid(_ phoneNumberString: String) -> Bool {
        do {
            let phoneNumber = try PhoneNumberKit().parse(phoneNumberString)
            let formattedPhoneNumber = PhoneNumberKit().format(phoneNumber, toType: .e164)
            print("Phone number is valid: \(formattedPhoneNumber)")
            return true
        } catch let error {
            print("Invalid phone number: \(error)")
            return false
        }
    }
}

这样就可以了,但不是很优雅。此外,用户输入与 UI 更改之间存在显着滞后。

另一件事是我上面的解决方案不符合 MVVM。我又试了一次。

class ViewController: UIViewController {
    @IBOutlet weak var textField: PhoneNumberTextField!
    @IBOutlet weak var submitButton: UIButton!

    private let viewModel = ViewModel()


    override func viewDidLoad() {
        super.viewDidLoad()
        textField.becomeFirstResponder()

        submitButton.reactive.isEnabled <~ viewModel.isPhoneNumberValid

        viewModel.phoneNumber <~ textField.reactive.continuousTextValues
    }
}

class ViewModel {
    let phoneNumber = MutableProperty("")
    let isPhoneNumberValid = MutableProperty(false)

    init() {
        isPhoneNumberValid = phoneNumber.producer.map { self.validatePhoneNumber([=13=]) } // Cannot assign value of type 'SignalProducer<Bool, NoError>' to type 'MutableProperty<Bool>'
    }

    private func validatePhoneNumber(_ phoneNumberString: String) -> Bool {
        do {
            let phoneNumber = try PhoneNumberKit().parse(phoneNumberString)
            let formattedPhoneNumber = PhoneNumberKit().format(phoneNumber, toType: .e164)
            print("Phone number is valid: \(formattedPhoneNumber)")
            return true
        } catch let error {
            print("Invalid phone number: \(error)")
            return false
        }
    }
}

当我将 validatePhoneNumber 函数的结果分配给 isPhoneNumberValid 属性.

时,初始化程序出现以下错误

Cannot assign value of type 'SignalProducer' to type 'MutableProperty'

我不知道如何将 phone 数字验证部分与提交按钮的 isEnabled 属性 和点击操作正确连接起来。

Demo project

尝试在 init 中设置 属性 并映射 属性 本身而不是生产者:

let isPhoneNumberValid: Property<Bool>

init() {
    isPhoneNumberValid = phoneNumber.map { ViewModel.validatePhoneNumber([=10=]) }
}

您必须将 validatePhoneNumber 设为静态方法,因为 self 尚不可用。

这是一种更典型的反应式方法,因为您在视图模型初始化期间完全根据另一个定义了一个 属性。