From e14ca2e94d4e995c552c2d7c417f52ad9c3b75d7 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 12 Dec 2015 20:00:33 +0100 Subject: [PATCH] Fixes problem with autocorrect and `rx_text` on `UITextView`. #333 --- .../iOS/Proxies/RxTextViewDelegateProxy.swift | 18 + RxCocoa/iOS/UITextView+Rx.swift | 21 +- .../APIWrappersViewController.swift | 10 + RxExample/RxExample/OSX/Main.storyboard | 16 +- RxExample/RxExample/iOS/Main.storyboard | 362 +++++++----------- 5 files changed, 197 insertions(+), 230 deletions(-) diff --git a/RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift b/RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift index 8be2dac4..cb33c039 100644 --- a/RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift +++ b/RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift @@ -26,6 +26,11 @@ public class RxTextViewDelegateProxy */ public weak private(set) var textView: UITextView? + /** + Internal event that captures all text changing events. + */ + internal let textChanging = PublishSubject() + /** Initializes `RxTextViewDelegateProxy` @@ -35,6 +40,19 @@ public class RxTextViewDelegateProxy self.textView = (parentObject as! UITextView) super.init(parentObject: parentObject) } + + // MARK: delegate methods + + /** + For more information take a look at `DelegateProxyType`. + */ + @objc public func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool { + let forwardToDelegate = self.forwardToDelegate() as? UITextViewDelegate + textChanging.onNext() + return forwardToDelegate?.textView?(textView, + shouldChangeTextInRange: range, + replacementText: text) ?? true + } } #endif diff --git a/RxCocoa/iOS/UITextView+Rx.swift b/RxCocoa/iOS/UITextView+Rx.swift index 5aae1de1..46f59eaf 100644 --- a/RxCocoa/iOS/UITextView+Rx.swift +++ b/RxCocoa/iOS/UITextView+Rx.swift @@ -31,11 +31,28 @@ extension UITextView { public var rx_text: ControlProperty { let source: Observable = deferred { [weak self] () -> Observable in let text = self?.text ?? "" - return (self?.rx_delegate.observe("textViewDidChange:") ?? empty()) + // basic event + let textChangedEvent = self?.rx_delegate.observe("textViewDidChange:") ?? empty() + + // Monitor all other events because text could change without user intervention and without + // `textViewDidChange:` firing. + // For example, autocorrecting spell checker. + let anyOtherEvent = (self?.rx_delegate as? RxTextViewDelegateProxy)?.textChanging ?? empty() + + // Throttle is here because textChanging will fire when text is about to change. + // Event needs to happen after text is changed. This is kind of a hacky way, but + // don't know any other way for now. + let throttledAnyOtherEvent = anyOtherEvent + .throttle(0, MainScheduler.sharedInstance) + .takeUntil(self?.rx_deallocated ?? just()) + + return sequenceOf(textChangedEvent.map { _ in () }, throttledAnyOtherEvent) + .merge() .map { a in - return (a[0] as? UITextView)?.text ?? "" + return self?.text ?? "" } .startWith(text) + .distinctUntilChanged() } return ControlProperty(values: source, valueSink: AnyObserver { [weak self] event in diff --git a/RxExample/RxExample/Examples/APIWrappers/APIWrappersViewController.swift b/RxExample/RxExample/Examples/APIWrappers/APIWrappersViewController.swift index 624df0d2..6c53e28e 100644 --- a/RxExample/RxExample/Examples/APIWrappers/APIWrappersViewController.swift +++ b/RxExample/RxExample/Examples/APIWrappers/APIWrappersViewController.swift @@ -51,6 +51,8 @@ class APIWrappersViewController: ViewController { @IBOutlet weak var mypan: UIPanGestureRecognizer! + @IBOutlet weak var textView: UITextView! + let manager = CLLocationManager() override func viewDidLoad() { @@ -198,6 +200,14 @@ class APIWrappersViewController: ViewController { .addDisposableTo(disposeBag) + // MARK: UITextView + + textView.rx_text + .subscribeNext { [weak self] x in + self?.debug("UITextView event \(x)") + } + .addDisposableTo(disposeBag) + // MARK: CLLocationManager #if !RX_NO_MODULE diff --git a/RxExample/RxExample/OSX/Main.storyboard b/RxExample/RxExample/OSX/Main.storyboard index 897bff83..64e20818 100644 --- a/RxExample/RxExample/OSX/Main.storyboard +++ b/RxExample/RxExample/OSX/Main.storyboard @@ -1,7 +1,7 @@ - + - + @@ -52,7 +52,6 @@ - @@ -67,7 +66,6 @@ - @@ -76,7 +74,6 @@ - @@ -85,7 +82,6 @@ - @@ -97,7 +93,6 @@ - @@ -106,7 +101,6 @@ - @@ -137,12 +128,10 @@ - - @@ -156,7 +145,6 @@ @@ -200,7 +185,6 @@ - @@ -213,20 +197,16 @@ - - - @@ -258,7 +238,6 @@ - @@ -267,9 +246,7 @@ - - @@ -277,7 +254,6 @@ - @@ -309,7 +285,6 @@ - @@ -319,7 +294,6 @@ - @@ -327,7 +301,6 @@ - @@ -370,39 +342,33 @@ - - - - - @@ -482,17 +445,14 @@ - - - @@ -510,16 +470,13 @@ - - @@ -535,13 +492,11 @@ - @@ -554,7 +509,6 @@ - @@ -595,7 +549,6 @@ - @@ -613,7 +566,6 @@ - @@ -1040,94 +946,155 @@ This is only showcase app, not intended for production purposes. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + - - - - + + + + + @@ -1146,6 +1113,7 @@ This is only showcase app, not intended for production purposes. + @@ -1167,7 +1135,6 @@ This is only showcase app, not intended for production purposes. - @@ -1177,7 +1144,6 @@ This is only showcase app, not intended for production purposes. - @@ -1187,7 +1153,6 @@ This is only showcase app, not intended for production purposes. - @@ -1197,14 +1162,12 @@ This is only showcase app, not intended for production purposes. - @@ -1213,13 +1176,11 @@ This is only showcase app, not intended for production purposes. - @@ -1263,7 +1224,6 @@ This is only showcase app, not intended for production purposes. - @@ -1641,7 +1580,6 @@ This is only showcase app, not intended for production purposes. - @@ -1654,20 +1592,16 @@ This is only showcase app, not intended for production purposes. - - -