首页 > 解决方案 > 使用复选框按钮 RxCocoa/RxSwift 验证表单

问题描述

我需要使用单选按钮验证表单,但我做不到,我分享我的代码:

我的观点:

private func registerForm() {
    tvUserName.rx.text.map { $0 ?? "" }.bind(to: viewModel.userNamePublishSubject).disposed(by: disposeBag)
    tvEmail.rx.text.map { $0 ?? "" }.bind(to: viewModel.emailPublishSubject).disposed(by: disposeBag)
    tvPassword.rx.text.map { $0 ?? "" }.bind(to: viewModel.passwordPublishSubject).disposed(by: disposeBag)
    tvRepeatPassword.rx.text.map { $0 ?? "" }.bind(to: viewModel.repeatPasswordPublishSubject).disposed(by: disposeBag)
    viewModel.formLoginNativaIsValid().bind(to: btnNext.rx.isEnabled).disposed(by: disposeBag)
    
    checkBoxOutlet.rx.tap
    .scan(false) { lastValue, _ in
        return !lastValue
    }
    .bind(to: btnNext.rx.isEnabled)
    .disposed(by: disposeBag)
}

我的视图模型:

let userNamePublishSubject = PublishSubject<String>()
let emailPublishSubject = PublishSubject<String>()
let passwordPublishSubject = PublishSubject<String>()
let repeatPasswordPublishSubject = PublishSubject<String>()

func formLoginNativaIsValid() -> Observable<Bool> {
    return Observable.combineLatest(userNamePublishSubject.asObserver(),
        emailPublishSubject.asObserver(),
        passwordPublishSubject.asObserver(),
        repeatPasswordPublishSubject.asObserver()).map { userName, email, password, repeatPassword in
            return self.validateRegister(userName: userName, email: email, password: password,
                                         repeatPassword: repeatPassword)
    }.startWith(false)
}

private func validateRegister(userName: String, email: String, password: String, repeatPassword: String) -> Bool {
    return userName.count >= 5 && email.isEmail() && password.count >= 5 && repeatPassword.count >= 5 && password == repeatPassword
}

我已经验证了表单,但我还需要用户接受启用“下一步”按钮的条款和条件。

标签: swiftformscheckboxrx-swiftrx-cocoa

解决方案


在设计反应式系统时,您需要考虑因果关系。每个可观察的链都应该关注一个单一的效果,并根据需要引入尽可能多的原因。就像一个函数有几个参数传入并返回一个值一样。

在这种情况下,您的效果是btnNext.rx.isEnabled。你必须考虑是什么导致这种影响发生变化?这是我将如何编写它的示例:

class View: UIView {
    weak var tvUserName: UITextView!
    weak var tvEmail: UITextView!
    weak var tvPassword: UITextView!
    weak var tvRepeatPassword: UITextView!
    weak var btnNext: UIButton!
    weak var checkBoxOutlet: UIButton!

    let disposeBag = DisposeBag()

    override func awakeFromNib() {
        super.awakeFromNib()

        isNextButtonEnabled(
            username: tvUserName.rx.text.orEmpty.asObservable(),
            email: tvEmail.rx.text.orEmpty.asObservable(),
            password: tvPassword.rx.text.orEmpty.asObservable(),
            repeatPassword: tvRepeatPassword.rx.text.orEmpty.asObservable(),
            checkBox: checkBoxOutlet.rx.tap.asObservable()
        )
        .bind(to: btnNext.rx.isEnabled)
        .disposed(by: disposeBag)
    }
}

func isNextButtonEnabled(username: Observable<String>, email: Observable<String>, password: Observable<String>, repeatPassword: Observable<String>, checkBox: Observable<Void>) -> Observable<Bool> {
    return Observable.combineLatest(
        [
            username.map { $0.count >= 5 },
            email.map { $0.isEmail() },
            password.map { $0.count >= 5 },
            Observable.combineLatest(password, repeatPassword) { $0 == $1 },
            checkBox.scan(false) { state, _ in !state }.startWith(false)
        ]
    )
    .map { $0.allSatisfy { $0 } }
}

请注意您的视图模型在这里是多么简单。就是isNextButtonEnabled功能。它可以很容易地测试,但非常简单,您甚至可能觉得不需要测试。确定下一个按钮是否启用的所有条件都集中在一个地方以便于理解。不需要一堆 PublishSubjects。它更容易阅读和理解。


推荐阅读