【问题标题】:How to convert Delegate to Observable RxSwift?如何将 Delegate 转换为 Observable RxSwift?
【发布时间】:2019-01-19 19:39:02
【问题描述】:

我有委托方法,我需要在 RxSwift 中由委托代理包装。我已经使用 Bond 和 Reactive 完成了它,但是在这里,在 RxSwift 中,我无法找到转换它的正确方法。

遵循协议

    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)
    }

我之前做的是

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)
        }
    }
}

如何在 RxSwift 中做同样的练习。 我尝试了 DelegateProxy,但不清楚它是如何正确包装它的。

【问题讨论】:

  • 如果你使用的是 RxSwift(观察),为什么还要继续使用委托?
  • 那么,在哪里调用或分配某个类的委托方法?
  • 如果你观察 Q,有四个委托,并且需要在主控制器中通过包装使用它们,但是我们如何在 Rx 中做同样的事情?

标签: swift rx-swift delegation


【解决方案1】:

由于这个答案很受欢迎,我写了一篇关于它的文章:Convert a Swift Delegate to RxSwift Observables

我相信这是将委托转换为 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 }
    }
}

具备以上条件后,您应该能够:

let validInfo: Observable<String> = myCardInfoTextField.rx.didEnterValidInfo

【讨论】:

  • 该死的。关键是将委托方法定义为可选,它还依赖于 Objective-C 运行时,这意味着您不能在方法中使用枚举。
  • Daniel,知道如何在不将委托协议暴露给 Objective-C 的情况下实现这一点吗?我正在尝试对未将@objc 添加到协议的第三方库执行此操作
  • 当然。如果委托协议没有暴露给 Objective-C,那么它就不能是可选的。在这种情况下,您必须在您的委托代理中实现所有功能并将它们中的每一个连接到它们自己的主题。在 Reactive 扩展中,您将 Subject 作为 Observable 返回。如果您将其作为问题发布,我可以提供一个示例,但也许以上内容足以让您继续。
  • @gfpacheco 我写了一篇文章详细说明了该怎么做:Convert a Swift Delegate to RxSwift Observables
  • 这不是问题,因为委托协议没有暴露给 Objective-C 我不能使用 Selector(或#selector),这意味着我也不能使用 delegate.methodInvoked .但是我通过子类化 lib 类并在其中创建反应性字段来使其工作。还是谢谢
【解决方案2】:

编辑

我已删除我之前的代码并将其调整为您想要的解决方案。为了将各种类(主要是 UI)的委托“包装”到可观察对象中,您可以使用 DelegateProxy 类,它是 RxCocoa 框架的一部分。

假设您的DetailInputTextField 类有一个delegate 类型为DetailInputTextFieldDelegate 的属性,示例如下:

首先是自定义代理:

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) }
    }
}

然后你需要扩展Reactive,你可以在其中添加与委托方法对应的所有可观察对象:

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)
        }
    }
}

当你实现了“包装器”后,你可以这样称呼它:

let textField = DetailInputTextField()
textField.rx.didEnterValidInfo
    .asObservable()
    .subscribe(onNext: { (textField: UITextField, string: String) in
        print("Test \(string)")
    })
    .disposed(by: disposeBag)

我希望它能回答你的问题。

【讨论】:

  • 这个 DetailInputTextField 不是我的课。它已经在图书馆的某个地方声明了,所以它不会生效吗?其次,什么是委托,它没有识别它
  • 如果DetailInputTextField 不是你的类,那么你不应该向它添加代码。我会更新我的答案。
  • 当然,我在等,我做了类似扩展 Reactive where Base: DetailInputTextField { public var dataSource: DelegateProxy { return RxDetailInputTextFieldProxy.proxy(for: base) } } ///有关更多信息,请查看DelegateProxyType。 open class RxDetailInputTextFieldProxy : DelegateProxy , DelegateProxyType , CardInfoTextFieldDelegate { 不过好像还不清楚。
  • 请查看我更新的答案。最初我想到了另一种方法,但这对我来说也是一个有趣的练习。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-23
  • 2021-11-28
  • 1970-01-01
相关资源
最近更新 更多