1

Я делаю проверку формы с использованием реактивной скорости. Но я столкнулся с проблемой сброса значения значения &.Сигналы и наблюдатели (Reactive Swift) для проверки формы не работают должным образом

Когда я заполняю все текстовое поле правильно, как указано в правиле проверки, все сигналы (текстовые значения сплошного контекста) создают истинное значение, которое позволит мне отправлять данные формы. Я возвращаю значения текстового поля после завершения отправки формы. После этого я отправляю ложное значение ко всему сигналу Observer. Но когда я начну заполнять текстовое поле, он получит предыдущий истинный сигнал и позволит мне отправлять данные без применения правила валидации. это означает, что я не могу сбросить значение сигнала

Любая помощь была бы действительно оценена.

Моя проблема:

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{ ($0?.characters.count ?? 0) >= 3 } 
     self.phoneValidationSignal = self.textFieldPhoneOL.reactive.continuousTextValues 
      .map{ ($0?.characters.count ?? 0) >= 8 } 
     self.emailValidationSignal = self.textFieldEmailOL.reactive.continuousTextValues 
      .map{ $0?.isEmail ?? false } 
     self.locationValidationSignal = self.textFieldLocationOL.reactive.continuousTextValues 
      .map{ ($0?.characters.count ?? 0) >= 3 } 
     self.commentValidationSignal = self.textViewComent.reactive.continuousTextValues 
      .map{ ($0?.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 [email protected]".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 { 
       $0.0.0.0 && $0.0.0.1 && $0.0.1 && $0.1 && $1 
     } 


     formValidationSignal.observeValues { 
       self.btnSubmitOL.isUserInteractionEnabled = $0 
       self.btnSubmitOL.backgroundColor = $0 ? UIColor.appRedColor() : UIColor.gray 
     } 
    } 

} 

Я сделал решение этой проблемы, но я не думаю, что это идеальный способ и реактивная не так, как я сделал, чтобы решить. Я жду идеального или самого приемлемого решения. Любая помощь или ответ действительно оценены.

ответ

6

Вот мой взгляд на это с более идиоматическим подходом (упрощенно только два входа для примера).

Во-первых, есть ViewModel, у которого есть MutableProperty s для хранения входных значений. Вы можете инициализировать эти значения чем угодно, кроме nil, если вы хотите получить другие начальные значения для входов.

ViewModel als обладает свойствами для проверки входов. Property.map используется для вывода действительных значений из ввода. Btw, вы можете использовать Signal.combineLatest(signal1, signal2, signal3, ...) вместо signal1.combineLatest(with: signal2).combineLatest(with: signal3)...

Наконец, есть Action, который выполняет представление. В ViewController мы можем привязать эту кнопку 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 ($0 ?? "").characters.count > 0 
     } 
     self.addressValid = address.map { 
      return ($0 ?? "").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) 
    } 
} 

Во-первых, MutableProperty s о ViewModel связаны с UITextField с. Таким образом, текстовые поля не только инициализируются начальными значениями свойств в ViewModel, но также обновляются, если свойства в ViewModel обновляются - таким образом вы можете сбросить их при выполнении действия отправки.

Затем continuousTextValues из UITextFields привязаны к свойствам ViewModel. Поскольку continuousTextValues не срабатывает, если текст установлен программно, только если он задан пользователем, это не создает цикл.

Наконец, CocoaAction используется для привязки действия submit к действию кнопки pressed. Функция inputTransformer используется для отправки текущих значений входов при каждом нажатии кнопки.

Вы также можете подписаться на отдельные свойства viewModel для определения ошибок проверки дисплея для пользователя здесь.

+0

Я делаю так, как вы намереваетесь, но не полностью, но все еще пытаетесь понять, как работает CocoaAction. Я думаю, что это лучший метод, который я нашел до этого времени. Я буду отмечать этот ответ как правильный, как только действие будет успешным. – rjndra

+0

Мне нравится думать о «Action» (https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Action.swift) в качестве своего рода «Factory Signal Producer Factory». Если вы знакомы с RAC 2.0, это эквивалентно RACCommand. Вы выполняете действие с помощью ввода, и оно возвращает вам SignalProducer, который выполняет фактическую работу и отправляет свои результаты в виде событий. – MeXx

+0

'CocoaAction' - это всего лишь обертка для активации действия UIKit. Вариант, который я использовал в примере, использует 'inputTransformer' для определения фактического ввода для действия каждый раз, когда он выполняется. – MeXx

0

Ожидание ответа для поддержки или для лучшего ответа.

Я попытался решить себя, как указано в вопросе.

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{ $0.characters.count >= 3 }.producer 
    self.phoneSignal = self.viewModel.phoneNumber.producer.map{ $0.characters.count >= 8 }.producer 
    self.emailSignal = self.viewModel.emailAddress.producer.map{ $0.isEmail }.producer 
    self.locationSignal = self.viewModel.location.producer.map{ $0.characters.count >= 3 }.producer 
    self.commentSignal = self.viewModel.comment.producer.map{ $0.characters.count >= 5 }.producer 

    // Signals for TextFields 
    self.textFieldName.reactive.continuousTextValues.skipNil() 
     .observeValues { self.viewModel.name.value = $0 } 
    self.textFieldPhoneOL.reactive.continuousTextValues.skipNil() 
     .observeValues { self.viewModel.phoneNumber.value = $0 } 
    self.textFieldEmailOL.reactive.continuousTextValues.skipNil() 
     .observeValues { self.viewModel.emailAddress.value = $0 } 
    self.textFieldLocationOL.reactive.continuousTextValues.skipNil() 
     .observeValues{ self.viewModel.location.value = $0 } 
    self.textViewComent.reactive.continuousTextValues.skipNil() 
     .observeValues { self.viewModel.comment.value = $0 } 

    // 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 [email protected]".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 { 
     $0.0.0.0 && $0.0.0.1 && $0.0.1 && $0.1 && $1 
    } 


    formValidationViewModelSignal.startWithValues { 
     self.btnSubmitOL.isUserInteractionEnabled = $0 
     self.btnSubmitOL.backgroundColor = $0 ? UIColor.appRedColor() : UIColor.gray 
    } 
    } 

ContactView Модель Класс

import Foundation 
import ReactiveSwift 

class ContactViewModel { 

    var name = MutableProperty("") 
    var phoneNumber = MutableProperty("") 
    var emailAddress = MutableProperty("") 
    var location = MutableProperty("") 
    var comment = MutableProperty("") 

} 
Смежные вопросы