diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 8f4445b7..0ae4fe64 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -1601,7 +1601,7 @@ C88254001B8A752B00B02D69 /* RxTableViewDataSourceProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewDataSourceProxy.swift; sourceTree = ""; }; C88254011B8A752B00B02D69 /* RxTableViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewDelegateProxy.swift; sourceTree = ""; }; C88254021B8A752B00B02D69 /* RxTextViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTextViewDelegateProxy.swift; sourceTree = ""; }; - C88254051B8A752B00B02D69 /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "UIBarButtonItem+Rx.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + C88254051B8A752B00B02D69 /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "UIBarButtonItem+Rx.swift"; sourceTree = ""; }; C88254061B8A752B00B02D69 /* UIButton+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIButton+Rx.swift"; sourceTree = ""; }; C88254071B8A752B00B02D69 /* UICollectionView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Rx.swift"; sourceTree = ""; }; C88254081B8A752B00B02D69 /* UIControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "UIControl+Rx.swift"; sourceTree = ""; }; diff --git a/RxCocoa/iOS/Proxies/RxSearchBarDelegateProxy.swift b/RxCocoa/iOS/Proxies/RxSearchBarDelegateProxy.swift index 7f41b5df..14b84cec 100644 --- a/RxCocoa/iOS/Proxies/RxSearchBarDelegateProxy.swift +++ b/RxCocoa/iOS/Proxies/RxSearchBarDelegateProxy.swift @@ -14,19 +14,33 @@ import UIKit import RxSwift #endif -class RxSearchBarDelegateProxy : DelegateProxy +public class RxSearchBarDelegateProxy : DelegateProxy , UISearchBarDelegate , DelegateProxyType { - class func currentDelegateFor(object: AnyObject) -> AnyObject? { + public class func currentDelegateFor(object: AnyObject) -> AnyObject? { let searchBar: UISearchBar = castOrFatalError(object) return searchBar.delegate } - class func setCurrentDelegate(delegate: AnyObject?, toObject object: AnyObject) { + public class func setCurrentDelegate(delegate: AnyObject?, toObject object: AnyObject) { let searchBar: UISearchBar = castOrFatalError(object) searchBar.delegate = castOptionalOrFatalError(delegate) } + + // MARK: Delegate proxy methods + +#if os(iOS) + /** + For more information take a look at `DelegateProxyType`. + */ + public override class func createProxyForObject(object: AnyObject) -> AnyObject { + let searchBar = (object as! UISearchBar) + + return castOrFatalError(searchBar.rx_createDelegateProxy()) + } +#endif + } #endif diff --git a/RxCocoa/iOS/UISearchBar+Rx.swift b/RxCocoa/iOS/UISearchBar+Rx.swift index 038aca36..73413dc0 100644 --- a/RxCocoa/iOS/UISearchBar+Rx.swift +++ b/RxCocoa/iOS/UISearchBar+Rx.swift @@ -18,6 +18,17 @@ import UIKit extension UISearchBar { +#if os(iOS) + /** + Factory method that enables subclasses to implement their own `rx_delegate`. + + - returns: Instance of delegate proxy that wraps `delegate`. + */ + public func rx_createDelegateProxy() -> RxSearchBarDelegateProxy { + return RxSearchBarDelegateProxy(parentObject: self) + } +#endif + /** Reactive wrapper for `delegate`. @@ -68,6 +79,30 @@ extension UISearchBar { return ControlProperty(values: source, valueSink: bindingObserver) } + +#if os(iOS) + /** + Reactive wrapper for delegate method `searchBarCancelButtonClicked`. + */ + public var rx_cancelButtonClicked: ControlEvent { + let source: Observable = rx_delegate.observe(#selector(UISearchBarDelegate.searchBarCancelButtonClicked(_:))) + .map { _ in + return () + } + return ControlEvent(events: source) + } +#endif + + /** + Reactive wrapper for delegate method `searchBarSearchButtonClicked`. + */ + public var rx_searchButtonClicked: ControlEvent { + let source: Observable = rx_delegate.observe(#selector(UISearchBarDelegate.searchBarSearchButtonClicked(_:))) + .map { _ in + return () + } + return ControlEvent(events: source) + } } #endif diff --git a/Tests/RxCocoaTests/DelegateProxyTest+UIKit.swift b/Tests/RxCocoaTests/DelegateProxyTest+UIKit.swift index 5840a3fd..ea9a9eb5 100644 --- a/Tests/RxCocoaTests/DelegateProxyTest+UIKit.swift +++ b/Tests/RxCocoaTests/DelegateProxyTest+UIKit.swift @@ -44,6 +44,14 @@ import XCTest optional func testEventHappened(value: Int) } +#if os(iOS) +@objc protocol UISearchBarDelegateSubclass + : UISearchBarDelegate + , TestDelegateProtocol { + optional func testEventHappened(value: Int) +} +#endif + @objc protocol UITextViewDelegateSubclass : UITextViewDelegate , TestDelegateProtocol { @@ -91,6 +99,16 @@ extension DelegateProxyTest { } } +// MARK: UISearchBar + +#if os(iOS) +extension DelegateProxyTest { + func test_UISearchBarDelegateExtension() { + performDelegateTest(UISearchBarSubclass(frame: CGRect.zero)) + } +} +#endif + // MARK: UITextView extension DelegateProxyTest { @@ -254,6 +272,38 @@ class UIScrollViewSubclass } } +#if os(iOS) +class ExtendSearchBarDelegateProxy + : RxSearchBarDelegateProxy + , UISearchBarDelegateSubclass { + weak private(set) var control: UISearchBarSubclass? + + required init(parentObject: AnyObject) { + self.control = (parentObject as! UISearchBarSubclass) + super.init(parentObject: parentObject) + } +} + +class UISearchBarSubclass + : UISearchBar + , TestDelegateControl { + + override func rx_createDelegateProxy() -> RxSearchBarDelegateProxy { + return ExtendSearchBarDelegateProxy(parentObject: self) + } + + func doThatTest(value: Int) { + (delegate as! TestDelegateProtocol).testEventHappened?(value) + } + + var test: Observable { + return rx_delegate + .observe(#selector(TestDelegateProtocol.testEventHappened(_:))) + .map { a in (a[0] as! NSNumber).integerValue } + } +} +#endif + class ExtendTextViewDelegateProxy : RxTextViewDelegateProxy , UITextViewDelegateSubclass { diff --git a/Tests/RxCocoaTests/UISearchBar+RxTests.swift b/Tests/RxCocoaTests/UISearchBar+RxTests.swift index 8c962db5..4c9b91f5 100644 --- a/Tests/RxCocoaTests/UISearchBar+RxTests.swift +++ b/Tests/RxCocoaTests/UISearchBar+RxTests.swift @@ -76,4 +76,44 @@ class UISearchBarTests : RxTest { _ = Observable.just(1).bindTo(searchBar.rx_selectedScopeButtonIndex) XCTAssertEqual(searchBar.selectedScopeButtonIndex, 1) } + +#if os(iOS) + func testCancelButtonClicked() { + let searchBar = UISearchBar(frame: CGRectMake(0, 0, 1, 1)) + + var tapped = false + + let _ = searchBar.rx_cancelButtonClicked.subscribeNext { _ in + tapped = true + } + + XCTAssertFalse(tapped) + searchBar.delegate!.searchBarCancelButtonClicked!(searchBar) + XCTAssertTrue(tapped) + } + + func testCancelButtonClicked_DelegateEventCompletesOnDealloc() { + let createView: () -> UISearchBar = { UISearchBar(frame: CGRectMake(0, 0, 1, 1)) } + ensureEventDeallocated(createView) { (view: UISearchBar) in view.rx_cancelButtonClicked } + } +#endif + + func testSearchButtonClicked() { + let searchBar = UISearchBar(frame: CGRectMake(0, 0, 1, 1)) + + var tapped = false + + let _ = searchBar.rx_searchButtonClicked.subscribeNext { _ in + tapped = true + } + + XCTAssertFalse(tapped) + searchBar.delegate!.searchBarSearchButtonClicked!(searchBar) + XCTAssertTrue(tapped) + } + + func testSearchButtonClicked_DelegateEventCompletesOnDealloc() { + let createView: () -> UISearchBar = { UISearchBar(frame: CGRectMake(0, 0, 1, 1)) } + ensureEventDeallocated(createView) { (view: UISearchBar) in view.rx_searchButtonClicked } + } } \ No newline at end of file