用于表单验证的信号和观察者(Reactive Swift)未按预期工作
Signals and Observer(Reactive Swift) for form validation not working as expected
我正在使用反应式 swift 进行表单验证。但是我在重置值和信号值时遇到了问题。
当我按照验证规则正确填写所有文本字段时,所有信号(文本字段连续文本值)都会产生真值,这将允许我发送表单数据。我在完成表单提交后重置了文本字段的值。之后我向所有信号观察者发送假值。但是,当我开始填充文本字段时,它将获得先前的真实信号,并允许我在不应用任何验证规则的情况下发送数据。这意味着我无法重置信号值
任何帮助将不胜感激。
我的问题:
import UIKit
import ReactiveSwift
import Result
class ContactVC: BaseViewController {
@IBOutlet weak var textFieldName: JVFloatLabeledTextField!
@IBOutlet weak var textFieldPhoneOL: JVFloatLabeledTextField!
@IBOutlet weak var textViewComent: UITextView!
@IBOutlet weak var textFieldLocationOL: JVFloatLabeledTextField!
@IBOutlet weak var textFieldEmailOL: JVFloatLabeledTextField!
@IBOutlet weak var btnSubmitOL: PGSpringAnimation!
var (nameValidationSignal, nameValidationObserver) = Signal<Bool, NoError>.pipe()
var (phoneValidationSignal, phoneValidationObserver) = Signal<Bool, NoError>.pipe()
var (emailValidationSignal, emailValidationObserver) = Signal<Bool, NoError>.pipe()
var (locationValidationSignal, locationValidationObserver) = Signal<Bool, NoError>.pipe()
var (commentValidationSignal, commentValidationObserver) = Signal<Bool, NoError>.pipe()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.formValidation()
}
// MARK: - submit button action
@IBAction func btnSubmitAction(_ sender: Any) {
let params = ["name":textFieldName.text!,"email":textFieldEmailOL.text!,"location":textFieldLocationOL.text!,"message":textViewComent.text!,"phone":textFieldPhoneOL.text!]
APIManager(urlString:enumUrl.ContactAdmin.mainURL(),parameters:params as [String : AnyObject]?,method: .post).handleResponse(viewController: self, progressMessage: "downloading", completionHandler: { (response : AllResponse) in
self.nameValidationObserver.send(value: false)
self.emailValidationObserver.send(value: false)
self.phoneValidationObserver.send(value: false)
self.locationValidationObserver.send(value: false)
self.commentValidationObserver.send(value: false)
self.btnSubmitOL.backgroundColor = UIColor.gray
self.btnSubmitOL.isUserInteractionEnabled = false
})
}
// MARK: - validation textfield
func formValidation(){
self.btnSubmitOL.backgroundColor = UIColor.gray
self.btnSubmitOL.isUserInteractionEnabled = false
// Create signals
// Signals for TextFields
self.nameValidationSignal = self.textFieldName.reactive.continuousTextValues
.map{ ([=11=]?.characters.count ?? 0) >= 3 }
self.phoneValidationSignal = self.textFieldPhoneOL.reactive.continuousTextValues
.map{ ([=11=]?.characters.count ?? 0 ) >= 8 }
self.emailValidationSignal = self.textFieldEmailOL.reactive.continuousTextValues
.map{ [=11=]?.isEmail ?? false }
self.locationValidationSignal = self.textFieldLocationOL.reactive.continuousTextValues
.map{ ([=11=]?.characters.count ?? 0) >= 3 }
self.commentValidationSignal = self.textViewComent.reactive.continuousTextValues
.map{ ([=11=]?.characters.count ?? 0) >= 5 }
// Observe TextFields Singals for Changing UI
self.nameValidationSignal.observeValues { value in
self.textFieldName.floatingLabelActiveTextColor = value ? UIColor.red : UIColor.black
self.textFieldName.floatingLabel.text = value ? "name".localize : "Name must be greater than 4 characters".localize
}
self.phoneValidationSignal.observeValues { value in
self.textFieldPhoneOL.floatingLabelActiveTextColor = value ? UIColor.red : UIColor.black
self.textFieldPhoneOL.floatingLabel.text = value ? "phone".localize : "Phone must be greater than 7 characters".localize
}
self.emailValidationSignal.observeValues { value in
self.textFieldEmailOL.floatingLabelActiveTextColor = value ? UIColor.red : UIColor.black
self.textFieldEmailOL.floatingLabel.text = value ? "email".localize : "Email must be of type example@test.com".localize
}
self.locationValidationSignal.observeValues { value in
self.textFieldLocationOL.floatingLabelActiveTextColor = value ? UIColor.red : UIColor.black
self.textFieldLocationOL.floatingLabel.text = value ? "location".localize : "Loation must be greater than 4 characters".localize
}
self.commentValidationSignal.observeValues { value in
self.textViewComent.textColor = value ? UIColor.red : UIColor.black
}
let formValidationSignal = nameValidationSignal.combineLatest(with: phoneValidationSignal).combineLatest(with: emailValidationSignal).combineLatest(with: locationValidationSignal).combineLatest(with: commentValidationSignal)
.map {
[=11=].0.0.0 && [=11=].0.0.1 && [=11=].0.1 && [=11=].1 &&
}
formValidationSignal.observeValues {
self.btnSubmitOL.isUserInteractionEnabled = [=11=]
self.btnSubmitOL.backgroundColor = [=11=] ? UIColor.appRedColor() : UIColor.gray
}
}
}
我已经解决了这个问题,但我认为这不是完美的方法,反应式也不是我解决的方法。
我正在等待完美或最被接受的解决方案。
非常感谢任何帮助或回答。
等待答案支持或更好的答案。
我尝试按照问题所述解决自己。
import UIKit
import ReactiveSwift
import Result
class ContactVC: BaseViewController {
@IBOutlet weak var textFieldName: JVFloatLabeledTextField!
@IBOutlet weak var textFieldPhoneOL: JVFloatLabeledTextField!
@IBOutlet weak var textViewComent: UITextView!
@IBOutlet weak var textFieldLocationOL: JVFloatLabeledTextField!
@IBOutlet weak var textFieldEmailOL: JVFloatLabeledTextField!
@IBOutlet weak var btnSubmitOL: PGSpringAnimation!
// Singals Start
var nameSignal:SignalProducer<Bool, NoError>!
var phoneSignal:SignalProducer<Bool, NoError>!
var emailSignal:SignalProducer<Bool, NoError>!
var locationSignal:SignalProducer<Bool, NoError>!
var commentSignal:SignalProducer<Bool, NoError>!
// Signals End
private var viewModel = ComtactViewModel()
override func viewDidLoad() {
super.viewDidLoad()
checkLocationAuthorizationStatus()
setupBindings()
}
func setupBindings() {
//binding to view model to UI
self.textFieldName.reactive.text <~ self.viewModel.name
self.textFieldPhoneOL.reactive.text <~ self.viewModel.phoneNumber
self.textFieldEmailOL.reactive.text <~ self.viewModel.emailAddress
self.textFieldLocationOL.reactive.text <~ self.viewModel.location
self.textViewComent.reactive.text <~ self.viewModel.comment
}
// MARK: - submit button action
@IBAction func btnSubmitAction(_ sender: Any) {
self.btnSubmitOL.isUserInteractionEnabled = false
let params = ["name":textFieldName.text!,"email":textFieldEmailOL.text!,"location":textFieldLocationOL.text!,"message":textViewComent.text!,"phone":textFieldPhoneOL.text!]
APIManager(urlString:enumUrl.ContactAdmin.mainURL(),parameters:params as [String : AnyObject]?,method: .post).handleResponse(viewController: self, progressMessage: "downloading", completionHandler: { (response : AllResponse) in
self.viewModel.name.value = ""
self.viewModel.phoneNumber.value = ""
self.viewModel.emailAddress.value = ""
self.viewModel.location.value = ""
self.viewModel.comment.value = ""
Utilities.showAlert(alertTitle: "sucess", alertMessage: response.message!, viewController: self, didTabOkButton: {
self.btnSubmitOL.backgroundColor = UIColor.gray
self.btnSubmitOL.isUserInteractionEnabled = false
}, didTabOnCancelButton: nil)
})
}
// MARK: - validation textfield
func formValidation(){
self.btnSubmitOL.backgroundColor = UIColor.gray
self.btnSubmitOL.isUserInteractionEnabled = false
// Create signals
// Signals for ViewModels for crossCheck
self.nameSignal = self.viewModel.name.producer.map{ [=10=].characters.count >= 3 }.producer
self.phoneSignal = self.viewModel.phoneNumber.producer.map{ [=10=].characters.count >= 8 }.producer
self.emailSignal = self.viewModel.emailAddress.producer.map{ [=10=].isEmail }.producer
self.locationSignal = self.viewModel.location.producer.map{ [=10=].characters.count >= 3 }.producer
self.commentSignal = self.viewModel.comment.producer.map{ [=10=].characters.count >= 5 }.producer
// Signals for TextFields
self.textFieldName.reactive.continuousTextValues.skipNil()
.observeValues { self.viewModel.name.value = [=10=] }
self.textFieldPhoneOL.reactive.continuousTextValues.skipNil()
.observeValues { self.viewModel.phoneNumber.value = [=10=] }
self.textFieldEmailOL.reactive.continuousTextValues.skipNil()
.observeValues { self.viewModel.emailAddress.value = [=10=] }
self.textFieldLocationOL.reactive.continuousTextValues.skipNil()
.observeValues{ self.viewModel.location.value = [=10=] }
self.textViewComent.reactive.continuousTextValues.skipNil()
.observeValues { self.viewModel.comment.value = [=10=] }
// Observe TextFields Singals for Changing UI
self.nameSignal.startWithValues { value in
self.textFieldName.textColor = value ? UIColor.appRedColor() : UIColor.black
self.textFieldName.floatingLabel.text = value ? "name".localize : "Name must be greater than 4 characters".localize
}
self.phoneSignal.startWithValues { value in
self.textFieldPhoneOL.textColor = value ? UIColor.appRedColor() : UIColor.black
self.textFieldPhoneOL.floatingLabel.text = value ? "phone".localize : "Phone must be greater than 7 characters".localize
}
self.emailSignal.startWithValues { value in
self.textFieldEmailOL.textColor = value ? UIColor.appRedColor() : UIColor.black
self.textFieldEmailOL.floatingLabel.text = value ? "email".localize : "Email must be of type example@test.com".localize
}
self.locationSignal.startWithValues { value in
self.textFieldLocationOL.textColor = value ? UIColor.appRedColor() : UIColor.black
self.textFieldLocationOL.floatingLabel.text = value ? "location".localize : "Loation must be greater than 4 characters".localize
}
self.commentSignal.startWithValues { value in
self.textViewComent.textColor = value ? UIColor.appRedColor() : UIColor.black
}
let formValidationViewModelSignal = self.nameSignal.combineLatest(with: self.phoneSignal).combineLatest(with: self.emailSignal).combineLatest(with: self.locationSignal).combineLatest(with: self.commentSignal).map {
[=10=].0.0.0 && [=10=].0.0.1 && [=10=].0.1 && [=10=].1 &&
}
formValidationViewModelSignal.startWithValues {
self.btnSubmitOL.isUserInteractionEnabled = [=10=]
self.btnSubmitOL.backgroundColor = [=10=] ? UIColor.appRedColor() : UIColor.gray
}
}
ContactView 模型Class
import Foundation
import ReactiveSwift
class ContactViewModel {
var name = MutableProperty("")
var phoneNumber = MutableProperty("")
var emailAddress = MutableProperty("")
var location = MutableProperty("")
var comment = MutableProperty("")
}
这是我对这个问题的一种更惯用的方法(为了示例简化为只有两个输入)。
首先,有一个 ViewModel 有 MutableProperty
s 来保存输入值。如果您想要输入的其他初始值,您可以将这些值初始化为 nil
以外的任何值。
ViewModel 还具有用于验证输入的属性。
Property.map
用于从输入推断有效值。顺便说一句,您可以使用 Signal.combineLatest(signal1, signal2, signal3, ...)
而不是 signal1.combineLatest(with: signal2).combineLatest(with: signal3)...
最后,Action
执行提交。在ViewController中,我们可以将这个Action
绑定到按钮上。 Action 每次执行时都会发送一个空字符串。动作的 .values
信号用于在执行动作后重置输入。
如果提交可能会产生错误,您应该相应地进行处理。
class ViewModel {
let username = MutableProperty<String?>(nil)
let address = MutableProperty<String?>(nil)
let usernameValid: Property<Bool>
let addressValid: Property<Bool>
let valid: Property<Bool>
let submit: Action<(String?, String?), String, NoError>
init() {
self.usernameValid = username.map {
return ([=10=] ?? "").characters.count > 0
}
self.addressValid = address.map {
return ([=10=] ?? "").characters.count > 0
}
self.valid = Property.combineLatest(self.usernameValid, self.addressValid).map { (usernameValid, addressValid) in
return usernameValid && addressValid
}
self.submit = Action(enabledIf: self.valid) { input in
print("Submit with username \(input.0) and address \(input.1)")
return SignalProducer<String, NoError>(value: "")
}
self.username <~ self.submit.values
self.address <~ self.submit.values
}
}
然后是 ViewController 中的设置:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.username.reactive.text <~ self.viewModel.username
self.address.reactive.text <~ self.viewModel.address
self.viewModel.username <~ self.username.reactive.continuousTextValues
self.viewModel.address <~ self.address.reactive.continuousTextValues
self.submit.reactive.pressed = CocoaAction(self.viewModel.submit) { [weak self] (button) -> (String?, String?) in
return (self?.username.text, self?.address.text)
}
}
首先,ViewModel 的 MutableProperty
绑定到 UITextField
。这样,文本字段不仅会初始化为 ViewModel 中属性的初始值,还会在 ViewModel 中的属性更新时更新它们 - 这样您就可以在执行提交操作时重置它们。
然后,UITextFields
的 continuousTextValues
绑定到 ViewModel 的属性。由于 continuousTextValues
如果以编程方式设置文本,则不会触发,只有当它由用户设置时,这不会创建循环。
最后,CocoaAction
用于将submit
动作绑定到按钮的pressed
动作。 inputTransformer
函数用于在每次按下按钮时发送输入的当前值。
您还可以订阅 viewModel 的单个 usernameValid
/ addressValid
属性,以在此处为用户设置显示验证错误。
我正在使用反应式 swift 进行表单验证。但是我在重置值和信号值时遇到了问题。
当我按照验证规则正确填写所有文本字段时,所有信号(文本字段连续文本值)都会产生真值,这将允许我发送表单数据。我在完成表单提交后重置了文本字段的值。之后我向所有信号观察者发送假值。但是,当我开始填充文本字段时,它将获得先前的真实信号,并允许我在不应用任何验证规则的情况下发送数据。这意味着我无法重置信号值
任何帮助将不胜感激。
我的问题:
import UIKit
import ReactiveSwift
import Result
class ContactVC: BaseViewController {
@IBOutlet weak var textFieldName: JVFloatLabeledTextField!
@IBOutlet weak var textFieldPhoneOL: JVFloatLabeledTextField!
@IBOutlet weak var textViewComent: UITextView!
@IBOutlet weak var textFieldLocationOL: JVFloatLabeledTextField!
@IBOutlet weak var textFieldEmailOL: JVFloatLabeledTextField!
@IBOutlet weak var btnSubmitOL: PGSpringAnimation!
var (nameValidationSignal, nameValidationObserver) = Signal<Bool, NoError>.pipe()
var (phoneValidationSignal, phoneValidationObserver) = Signal<Bool, NoError>.pipe()
var (emailValidationSignal, emailValidationObserver) = Signal<Bool, NoError>.pipe()
var (locationValidationSignal, locationValidationObserver) = Signal<Bool, NoError>.pipe()
var (commentValidationSignal, commentValidationObserver) = Signal<Bool, NoError>.pipe()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.formValidation()
}
// MARK: - submit button action
@IBAction func btnSubmitAction(_ sender: Any) {
let params = ["name":textFieldName.text!,"email":textFieldEmailOL.text!,"location":textFieldLocationOL.text!,"message":textViewComent.text!,"phone":textFieldPhoneOL.text!]
APIManager(urlString:enumUrl.ContactAdmin.mainURL(),parameters:params as [String : AnyObject]?,method: .post).handleResponse(viewController: self, progressMessage: "downloading", completionHandler: { (response : AllResponse) in
self.nameValidationObserver.send(value: false)
self.emailValidationObserver.send(value: false)
self.phoneValidationObserver.send(value: false)
self.locationValidationObserver.send(value: false)
self.commentValidationObserver.send(value: false)
self.btnSubmitOL.backgroundColor = UIColor.gray
self.btnSubmitOL.isUserInteractionEnabled = false
})
}
// MARK: - validation textfield
func formValidation(){
self.btnSubmitOL.backgroundColor = UIColor.gray
self.btnSubmitOL.isUserInteractionEnabled = false
// Create signals
// Signals for TextFields
self.nameValidationSignal = self.textFieldName.reactive.continuousTextValues
.map{ ([=11=]?.characters.count ?? 0) >= 3 }
self.phoneValidationSignal = self.textFieldPhoneOL.reactive.continuousTextValues
.map{ ([=11=]?.characters.count ?? 0 ) >= 8 }
self.emailValidationSignal = self.textFieldEmailOL.reactive.continuousTextValues
.map{ [=11=]?.isEmail ?? false }
self.locationValidationSignal = self.textFieldLocationOL.reactive.continuousTextValues
.map{ ([=11=]?.characters.count ?? 0) >= 3 }
self.commentValidationSignal = self.textViewComent.reactive.continuousTextValues
.map{ ([=11=]?.characters.count ?? 0) >= 5 }
// Observe TextFields Singals for Changing UI
self.nameValidationSignal.observeValues { value in
self.textFieldName.floatingLabelActiveTextColor = value ? UIColor.red : UIColor.black
self.textFieldName.floatingLabel.text = value ? "name".localize : "Name must be greater than 4 characters".localize
}
self.phoneValidationSignal.observeValues { value in
self.textFieldPhoneOL.floatingLabelActiveTextColor = value ? UIColor.red : UIColor.black
self.textFieldPhoneOL.floatingLabel.text = value ? "phone".localize : "Phone must be greater than 7 characters".localize
}
self.emailValidationSignal.observeValues { value in
self.textFieldEmailOL.floatingLabelActiveTextColor = value ? UIColor.red : UIColor.black
self.textFieldEmailOL.floatingLabel.text = value ? "email".localize : "Email must be of type example@test.com".localize
}
self.locationValidationSignal.observeValues { value in
self.textFieldLocationOL.floatingLabelActiveTextColor = value ? UIColor.red : UIColor.black
self.textFieldLocationOL.floatingLabel.text = value ? "location".localize : "Loation must be greater than 4 characters".localize
}
self.commentValidationSignal.observeValues { value in
self.textViewComent.textColor = value ? UIColor.red : UIColor.black
}
let formValidationSignal = nameValidationSignal.combineLatest(with: phoneValidationSignal).combineLatest(with: emailValidationSignal).combineLatest(with: locationValidationSignal).combineLatest(with: commentValidationSignal)
.map {
[=11=].0.0.0 && [=11=].0.0.1 && [=11=].0.1 && [=11=].1 &&
}
formValidationSignal.observeValues {
self.btnSubmitOL.isUserInteractionEnabled = [=11=]
self.btnSubmitOL.backgroundColor = [=11=] ? UIColor.appRedColor() : UIColor.gray
}
}
}
我已经解决了这个问题,但我认为这不是完美的方法,反应式也不是我解决的方法。 我正在等待完美或最被接受的解决方案。 非常感谢任何帮助或回答。
等待答案支持或更好的答案。
我尝试按照问题所述解决自己。
import UIKit
import ReactiveSwift
import Result
class ContactVC: BaseViewController {
@IBOutlet weak var textFieldName: JVFloatLabeledTextField!
@IBOutlet weak var textFieldPhoneOL: JVFloatLabeledTextField!
@IBOutlet weak var textViewComent: UITextView!
@IBOutlet weak var textFieldLocationOL: JVFloatLabeledTextField!
@IBOutlet weak var textFieldEmailOL: JVFloatLabeledTextField!
@IBOutlet weak var btnSubmitOL: PGSpringAnimation!
// Singals Start
var nameSignal:SignalProducer<Bool, NoError>!
var phoneSignal:SignalProducer<Bool, NoError>!
var emailSignal:SignalProducer<Bool, NoError>!
var locationSignal:SignalProducer<Bool, NoError>!
var commentSignal:SignalProducer<Bool, NoError>!
// Signals End
private var viewModel = ComtactViewModel()
override func viewDidLoad() {
super.viewDidLoad()
checkLocationAuthorizationStatus()
setupBindings()
}
func setupBindings() {
//binding to view model to UI
self.textFieldName.reactive.text <~ self.viewModel.name
self.textFieldPhoneOL.reactive.text <~ self.viewModel.phoneNumber
self.textFieldEmailOL.reactive.text <~ self.viewModel.emailAddress
self.textFieldLocationOL.reactive.text <~ self.viewModel.location
self.textViewComent.reactive.text <~ self.viewModel.comment
}
// MARK: - submit button action
@IBAction func btnSubmitAction(_ sender: Any) {
self.btnSubmitOL.isUserInteractionEnabled = false
let params = ["name":textFieldName.text!,"email":textFieldEmailOL.text!,"location":textFieldLocationOL.text!,"message":textViewComent.text!,"phone":textFieldPhoneOL.text!]
APIManager(urlString:enumUrl.ContactAdmin.mainURL(),parameters:params as [String : AnyObject]?,method: .post).handleResponse(viewController: self, progressMessage: "downloading", completionHandler: { (response : AllResponse) in
self.viewModel.name.value = ""
self.viewModel.phoneNumber.value = ""
self.viewModel.emailAddress.value = ""
self.viewModel.location.value = ""
self.viewModel.comment.value = ""
Utilities.showAlert(alertTitle: "sucess", alertMessage: response.message!, viewController: self, didTabOkButton: {
self.btnSubmitOL.backgroundColor = UIColor.gray
self.btnSubmitOL.isUserInteractionEnabled = false
}, didTabOnCancelButton: nil)
})
}
// MARK: - validation textfield
func formValidation(){
self.btnSubmitOL.backgroundColor = UIColor.gray
self.btnSubmitOL.isUserInteractionEnabled = false
// Create signals
// Signals for ViewModels for crossCheck
self.nameSignal = self.viewModel.name.producer.map{ [=10=].characters.count >= 3 }.producer
self.phoneSignal = self.viewModel.phoneNumber.producer.map{ [=10=].characters.count >= 8 }.producer
self.emailSignal = self.viewModel.emailAddress.producer.map{ [=10=].isEmail }.producer
self.locationSignal = self.viewModel.location.producer.map{ [=10=].characters.count >= 3 }.producer
self.commentSignal = self.viewModel.comment.producer.map{ [=10=].characters.count >= 5 }.producer
// Signals for TextFields
self.textFieldName.reactive.continuousTextValues.skipNil()
.observeValues { self.viewModel.name.value = [=10=] }
self.textFieldPhoneOL.reactive.continuousTextValues.skipNil()
.observeValues { self.viewModel.phoneNumber.value = [=10=] }
self.textFieldEmailOL.reactive.continuousTextValues.skipNil()
.observeValues { self.viewModel.emailAddress.value = [=10=] }
self.textFieldLocationOL.reactive.continuousTextValues.skipNil()
.observeValues{ self.viewModel.location.value = [=10=] }
self.textViewComent.reactive.continuousTextValues.skipNil()
.observeValues { self.viewModel.comment.value = [=10=] }
// Observe TextFields Singals for Changing UI
self.nameSignal.startWithValues { value in
self.textFieldName.textColor = value ? UIColor.appRedColor() : UIColor.black
self.textFieldName.floatingLabel.text = value ? "name".localize : "Name must be greater than 4 characters".localize
}
self.phoneSignal.startWithValues { value in
self.textFieldPhoneOL.textColor = value ? UIColor.appRedColor() : UIColor.black
self.textFieldPhoneOL.floatingLabel.text = value ? "phone".localize : "Phone must be greater than 7 characters".localize
}
self.emailSignal.startWithValues { value in
self.textFieldEmailOL.textColor = value ? UIColor.appRedColor() : UIColor.black
self.textFieldEmailOL.floatingLabel.text = value ? "email".localize : "Email must be of type example@test.com".localize
}
self.locationSignal.startWithValues { value in
self.textFieldLocationOL.textColor = value ? UIColor.appRedColor() : UIColor.black
self.textFieldLocationOL.floatingLabel.text = value ? "location".localize : "Loation must be greater than 4 characters".localize
}
self.commentSignal.startWithValues { value in
self.textViewComent.textColor = value ? UIColor.appRedColor() : UIColor.black
}
let formValidationViewModelSignal = self.nameSignal.combineLatest(with: self.phoneSignal).combineLatest(with: self.emailSignal).combineLatest(with: self.locationSignal).combineLatest(with: self.commentSignal).map {
[=10=].0.0.0 && [=10=].0.0.1 && [=10=].0.1 && [=10=].1 &&
}
formValidationViewModelSignal.startWithValues {
self.btnSubmitOL.isUserInteractionEnabled = [=10=]
self.btnSubmitOL.backgroundColor = [=10=] ? UIColor.appRedColor() : UIColor.gray
}
}
ContactView 模型Class
import Foundation
import ReactiveSwift
class ContactViewModel {
var name = MutableProperty("")
var phoneNumber = MutableProperty("")
var emailAddress = MutableProperty("")
var location = MutableProperty("")
var comment = MutableProperty("")
}
这是我对这个问题的一种更惯用的方法(为了示例简化为只有两个输入)。
首先,有一个 ViewModel 有 MutableProperty
s 来保存输入值。如果您想要输入的其他初始值,您可以将这些值初始化为 nil
以外的任何值。
ViewModel 还具有用于验证输入的属性。
Property.map
用于从输入推断有效值。顺便说一句,您可以使用 Signal.combineLatest(signal1, signal2, signal3, ...)
而不是 signal1.combineLatest(with: signal2).combineLatest(with: signal3)...
最后,Action
执行提交。在ViewController中,我们可以将这个Action
绑定到按钮上。 Action 每次执行时都会发送一个空字符串。动作的 .values
信号用于在执行动作后重置输入。
如果提交可能会产生错误,您应该相应地进行处理。
class ViewModel {
let username = MutableProperty<String?>(nil)
let address = MutableProperty<String?>(nil)
let usernameValid: Property<Bool>
let addressValid: Property<Bool>
let valid: Property<Bool>
let submit: Action<(String?, String?), String, NoError>
init() {
self.usernameValid = username.map {
return ([=10=] ?? "").characters.count > 0
}
self.addressValid = address.map {
return ([=10=] ?? "").characters.count > 0
}
self.valid = Property.combineLatest(self.usernameValid, self.addressValid).map { (usernameValid, addressValid) in
return usernameValid && addressValid
}
self.submit = Action(enabledIf: self.valid) { input in
print("Submit with username \(input.0) and address \(input.1)")
return SignalProducer<String, NoError>(value: "")
}
self.username <~ self.submit.values
self.address <~ self.submit.values
}
}
然后是 ViewController 中的设置:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.username.reactive.text <~ self.viewModel.username
self.address.reactive.text <~ self.viewModel.address
self.viewModel.username <~ self.username.reactive.continuousTextValues
self.viewModel.address <~ self.address.reactive.continuousTextValues
self.submit.reactive.pressed = CocoaAction(self.viewModel.submit) { [weak self] (button) -> (String?, String?) in
return (self?.username.text, self?.address.text)
}
}
首先,ViewModel 的 MutableProperty
绑定到 UITextField
。这样,文本字段不仅会初始化为 ViewModel 中属性的初始值,还会在 ViewModel 中的属性更新时更新它们 - 这样您就可以在执行提交操作时重置它们。
然后,UITextFields
的 continuousTextValues
绑定到 ViewModel 的属性。由于 continuousTextValues
如果以编程方式设置文本,则不会触发,只有当它由用户设置时,这不会创建循环。
最后,CocoaAction
用于将submit
动作绑定到按钮的pressed
动作。 inputTransformer
函数用于在每次按下按钮时发送输入的当前值。
您还可以订阅 viewModel 的单个 usernameValid
/ addressValid
属性,以在此处为用户设置显示验证错误。