I found a my unintended behavior in this case.
let subscription = textField.rx_text.subscribeNext { print($0) }
textField.text = "It is never observed in subscription馃様"
By the way, UITextView.rx_text can observe changes of text programmatically.
let subscription = textView.rx_text.subscribeNext { print($0) }
textView.text = "It is observed in subscription馃槃"
It is caused by the difference of implement these.
UITextField.rx_text use UIControl.rx_value, and UIControl.rx_value seems observe changes caused only UIEvent.
https://github.com/ReactiveX/RxSwift/blob/698a50b381ef1323a153bb065adac0c37b5f1755/RxCocoa/iOS/UIControl%2BRx.swift#L71
I read the discussion #471, I think we need to discussion continue.
There is the same type but these contain different behavior.
I think it would lead to misunderstanding.(least, UITextField and UITextView)
I implemented UIControl.rx_value with KVO event. (#552)
I think it may work well.
Hi @tarunon this does not work because it's not a control event.
And as far as I remember its not possible to observe text property via KVO
The only "hack" that you can make is to tell manually to UIControl that value has changed
via sendActionsForControlEvents then it will notify your observers
Hi @sergdort san,
Thanks for your point 馃槃
Yes, KVO does not observe the value change caused UIEvent.
So, I implemented combine KVO and ControlEvent now.
Do you know other problem and pointed that?
@tarunon
Well, I thing that values in the UITextField and UITextView are always reflection of some values of Model or ViewModel layer
So I don't understand why programmatic changes of text property should "send" events
But rather changes of view model should change the value of the text via some bindings and vice versa
@sergdort
Hmm, It is true, I think it's beautiful design and we should make so.
But this problem is about API.
I think it would lead misunderstanding that same name parameters of the same type have a different behavior.
@tarunon
From my perspective:
Let's imagine you work in traditional way without Rx, to achieve rx_text behavior you would need to implement delegate or add target for control events. And in this case you also will not be notified if you would change the value of the text property programmatically
Since rx_text is ControlProperty I don't see problem in API, because "event sending" based on the user interaction
Hi guys,
If UITextField and UITextView behave differently then we would need to unify them. We would want to be consistent with platform and just report user initiated changes.
If I understood what @tarunon is saying, I think part of the problem with UITextView might be that we've used a different API to observe changes, and that API might not be only reporting user initiated changes.
The reason why we've used that API there is because we've tried to solve problems with autocorrect and the current solution seemed to remedy that situation.
I think we'll maybe need to return that code to more hacky form we had before :(. Looks like I will need to investigate that one again :(
@kzaher
Thank you.
If my understanding is correct, this discussion is about UITextView.rx_text observe programmatically change.
And my #552 pr will not be required, I'll close it. 馃槃
Should I change this issues title?
FWIW, I think a problem I'm having is due to this.
I posted about it to the RxSwift Slack.
Hello. I'm new to RxSwift, diving in for the first time.
Trying to figure out how to keep 2 UITextFields in sync, with validation intact.
I have a "Login/Join" ViewController, that has 2 views: 1 for new users to sign up, 1 for existing users to log in. Two fields in common between the views are the "username" and "password" fields (unique UITextField instances). Designs are to keep them in sync, so if a user starts typing a username on the Login view then switches to the Join view, the Join view's username field is pre-filled with the Login view's username field value.
Following the "SimpleValidationViewController" example, I've gotten things working. And it seems I can keep the fields in sync doing something like:
loginEmailField.rx_text.bindTo(joinEmailField.rx_text).addDisposableTo(disposeBag) joinEmailField.rx_text.bindTo(loginEmailField.rx_text).addDisposableTo(disposeBag)And while that keeps the fields in sync, the validation on the field (just like in the sample code, a
mapto ensure at least 1 character entered) does not kick in. So if I enter a valid username on the Join view and switch to Login, while the username field is filled in, none of the validation bindings have kicked in (and my "Login/Join" button doesn't enable). If I tap into the username field, validations kick in -- but that's not desireable.What am I missing to make the validations work?
Thank you.
I'm mentioning it here in case 1. it is directly related, 2. to provide a use case.
When I originally started to implement my setup (again, I'm a RxSwift n00b so I was doing it piece by piece), when in code I'd switch from one view to the other I simply did something like:
textField1.text = textField2.text
And expected it would work. Yes text was transfered over, but none of the extra validations, etc.
So I tried the above binding and that too works (and seems like the more Rx-way to do things), but again it just transfers the text, not the validations.
Edited: I tried @sergdort 's sendActionsForControlEvents suggestion, and it works (I put it where my user switches from one view to the other). From what @fpillet mentioned to me on Slack, it sounded like "this is how it is". If so, that's fair, but it does seem unexpected. And I'll agree with @kzaher 's desire for consistency. It may not be possible, but then it'd be at least welcome to document it as such.
Hi @tarunon ,
I think we should probably change title to something related with UITextView and stopping propagating programatic updates for rx_text, if I understood the issue correctly :)
Hi @hsoi ,
it is my understanding this code sample
joinEmailField.rx_text.bindTo(loginEmailField.rx_text).addDisposableTo(disposeBag)
works as advertised :)
Thnx for trying to help, but I think this is another issue :)
@kzaher well yes that works in terms of keeping the texts in sync. BUT it does not trigger the validations and other things. That's the problem and does seem to be the same issue.
I worked around it by using the sendActionsForControlEvents(.ValueChanged) workaround, which... works around it. But I agree with your statement that having consistency and unity is the way to go.
Hi guys,
I've checked current UITextView.rx_text behavior.
For this code
textView.rx_text.asObservable()
.subscribeNext { [weak self] x in
self?.debug("UITextView text \(x)")
self!.textView.text = "c"
}
.addDisposableTo(disposeBag)
textView.text = "1223423"
textView.text = "a"
textView.text = "b"
textView.text = "c"
... self?.debug("UITextView text \(x)") will only be called once and it will print
"UITextView text c"
If we can somehow improve this behavior and make it even more consistent, that would be great, but I don't think it's significantly different then other rx_text behaviors, although I can see how emitting that programmatically changed value can be little confusing.
The reason why it's like this is because UITextView has a quirk with autocorrect, and it behaves different then UITextField, so we couldn't just rely on UIControlEventValueChanged.
@hsoi I think there is a better way then sendActionsForControlEvents(.ValueChanged) if this creates a problem for you :)
If you really want it to behave like a variable you can just bind it to a Variable.
let textValue = Variable("")
textField.rx_text <-> textValue
... and then just work with textValue variable which will behave like you expect.
This looks stale to me. I think we can close this one. We can reopen it if needed.
@kzaher I did as you say
let textValue = Variable("")
textField.rx.textInput <-> textValue
but have some trouble after textField.resignFirstResponder(). the textField.rx.text whould not respond to the change of textValue.
Does anyone came with a solution to observe only user input without any programatic text change like on textfields ?
sendActions on the text field.
What a quick response :) looking into it
Sorry my question wasn't clear. I need to observe user only inputs on a UITextView. The behaviour works on a UITextField. So I can't use sendActions.
@AlexisQapa, try this
_ = textView.rx.didChange.map { [weak self] in
return self?.textView.text
}.subscribe(onNext: { text in
print("didChange text ", text)
})
Hi, I'm having the same issue here. So far, I think @giosad suggestion is working for me, but then, besides the first one emitting programmatic changes, what's the difference between:
textView.rx.text
and
textView.rx.didChange.map { textView.text }
?
I'm not being able to replicate any issues with autocorrect in the second case. Maybe just a really weird edge case?
Ok, don't worry, now I get it. The problem happens when you tap outside the text view while there's a "pending" auto-correction. In that case, didChange doesn't emit.
Most helpful comment
Hi @tarunon this does not work because it's not a control event.
And as far as I remember its not possible to observe
textproperty via KVOThe only "hack" that you can make is to tell manually to
UIControlthat value has changedvia
sendActionsForControlEventsthen it will notify your observers