问题
I have delegate methods, which I need to wrap by Delegate Proxy in RxSwift. I have done it using Bond and Reactive, but here, in RxSwift, I am not able to find the proper way to convert it.
Follow is Protocols
import UIKit
/**
A protocol for the delegate of a `DetailInputTextField`.
*/
@objc
public protocol CardInfoTextFieldDelegate {
/**
Called whenever valid information was entered into `textField`.
- parameter textField: The text field whose information was updated and is valid.
- parameter didEnterValidInfo: The valid information that was entered into `textField`.
*/
func textField(_ textField: UITextField, didEnterValidInfo: String)
/**
Called whenever partially valid information was entered into `textField`.
- parameter textField: The text field whose information was updated and is partially valid.
- parameter didEnterPartiallyValidInfo: The partially valid information that was entered.
*/
func textField(_ textField: UITextField, didEnterPartiallyValidInfo: String)
/**
Called whenever more text was entered into `textField` than necessary. This can be used to provide this overflow as text in the next text field in the responder chain.
- parameter textField: The text field which received more information than required.
- parameter overFlowDigits: The overflow of text which does not fit into `textField` and might be entered into the next receiver in the responder chain.
*/
func textField(_ textField: UITextField, didEnterOverflowInfo overFlowDigits: String)
}
What I did earlier is
import Foundation
import Bond
import Caishen
extension DetailInputTextField {
var bnd_cardInfoDelegate: ProtocolProxy {
return protocolProxy(for: CardInfoTextFieldDelegate.self, setter: NSSelectorFromString("setCardInfoTextFieldDelegate:"))
}
var bnd_didEnterValidInfo: StreamSignal<NSString> {
return bnd_cardInfoDelegate.signal(for: #selector(CardInfoTextFieldDelegate.textField(_:didEnterValidInfo:)))
{ (s: PublishSignal<NSString>, _: UITextField, info: NSString) in
s.next(info)
}
}
var bnd_didEnterPartiallyValidInfo: StreamSignal<NSString> {
return bnd_cardInfoDelegate.signal(for: #selector(CardInfoTextFieldDelegate.textField(_:didEnterPartiallyValidInfo:)))
{ (s: PublishSignal<NSString>, _: UITextField, info: NSString) in
s.next(info)
}
}
var bnd_didEnterOverflowInfo: StreamSignal<NSString> {
return bnd_cardInfoDelegate.signal(for: #selector(CardInfoTextFieldDelegate.textField(_:didEnterOverflowInfo:)))
{ (s: PublishSignal<NSString>, _: UITextField, info: NSString) in
s.next(info)
}
}
}
How can I do same exercise in RxSwift. I tried DelegateProxy but its unclear how it properly wrap it.
回答1:
I believe this is the official way of converting a delegate into RxObservables:
class CardInfoTextField: NSObject {
weak var delegate: CardInfoTextFieldDelegate? = nil
}
@objc
protocol CardInfoTextFieldDelegate {
@objc optional func textField(_ textField: CardInfoTextField, didEnterValidInfo: String)
@objc optional func textField(_ textField: CardInfoTextField, didEnterPartiallyValidInfo: String)
@objc optional func textField(_ textField: CardInfoTextField, didEnterOverflowInfo overFlowDigits: String)
}
extension CardInfoTextField: HasDelegate {
public typealias Delegate = CardInfoTextFieldDelegate
}
class CardInfoTextFieldDelegateProxy
: DelegateProxy<CardInfoTextField, CardInfoTextFieldDelegate>
, DelegateProxyType
, CardInfoTextFieldDelegate {
//#MARK: DelegateProxy
init(parentObject: CardInfoTextField) {
super.init(parentObject: parentObject, delegateProxy: CardInfoTextFieldDelegateProxy.self)
}
public static func registerKnownImplementations() {
self.register { CardInfoTextFieldDelegateProxy(parentObject: $0) }
}
}
extension Reactive where Base: CardInfoTextField {
var delegate: CardInfoTextFieldDelegateProxy {
return CardInfoTextFieldDelegateProxy.proxy(for: base)
}
var didEnterValidInfo: Observable<String> {
return delegate.methodInvoked(#selector(CardInfoTextFieldDelegate.textField(_:didEnterValidInfo:)))
.map { $0[1] as! String }
}
var didEnterPartiallyValidInfo: Observable<String> {
return delegate.methodInvoked(#selector(CardInfoTextFieldDelegate.textField(_:didEnterPartiallyValidInfo:)))
.map { $0[1] as! String }
}
var didEnterOverflowInfo: Observable<String> {
return delegate.methodInvoked(#selector(CardInfoTextFieldDelegate.textField(_:didEnterOverflowInfo:)))
.map { $0[1] as! String }
}
}
Once you have the above, you should be able to:
let validInfo: Observable<String> = myCardInfoTextField.rx.didEnterValidInfo
回答2:
EDIT
I've removed my previous code and adjusted it to your desired solution. In order to "wrap" delegates of various classes (mostly UI) into observables you can use DelegateProxy
class which is a part of RxCocoa
framework.
Assuming your DetailInputTextField
class has a property delegate
of type DetailInputTextFieldDelegate
here is the example:
First the custom proxy:
import RxSwift
import RxCocoa
extension DetailInputTextField: HasDelegate {
public typealias Delegate = DetailInputTextFieldDelegate
}
open class DetailInputTextFieldDelegateProxy
: DelegateProxy<DetailInputTextField, DetailInputTextFieldDelegate>
, DelegateProxyType
, DetailInputTextFieldDelegate {
/// Typed parent object.
public weak private(set) var textField: DetailInputTextField?
/// - parameter webView: Parent object for delegate proxy.
public init(textField: ParentObject) {
self.textField = textField
super.init(parentObject: textField, delegateProxy: DetailInputTextFieldDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { DetailInputTextFieldDelegateProxy(textField: $0) }
}
}
Then you need to extend Reactive
where you can add all the observables corresponding to delegate methods:
extension Reactive where Base: DetailInputTextField {
public var delegate: DelegateProxy<DetailInputTextField, DetailInputTextFieldDelegate> {
return DetailInputTextFieldDelegateProxy.proxy(for: base)
}
public var didEnterValidInfo: Observable<(UITextField,String)> {
return delegate
.methodInvoked(#selector(DetailInputTextFieldDelegate.textField(_:didEnterValidInfo:)))
.map { params in
// Parameters is an array, be sure you cast them correctly
return (params[0] as! UITextField, params[1] as! String)
}
}
}
When you have the "wrapper" implemented, you can call it like this:
let textField = DetailInputTextField()
textField.rx.didEnterValidInfo
.asObservable()
.subscribe(onNext: { (textField: UITextField, string: String) in
print("Test \(string)")
})
.disposed(by: disposeBag)
I hope it answers your question.
来源:https://stackoverflow.com/questions/51816144/how-to-convert-delegate-to-observable-rxswift