RxSwift实现替换delegate的方法示例

目标

最近写项目 ,写到需要为自己写的一个控件添加rx订阅方式的案例。

目前有一个代理:

// 代理方式获取结果
@objc public protocol ZZPhotoPickerControllerDelegate : NSObjectProtocol {
 @objc optional func photoPickerController(_ photoPickerController: ZZPhotoPickerController, didSelect assets: [Any])
}

需要写一个能够实现下边这种方式的扩展

photoPickerController.rx.assetsSelected.subscribe(onNext: { assets in
 // do something
}

思路

刚开始完全摸不着头脑。后来想到Rx写了对UICollectionViewDelegate的扩展:

collectionView.rx.itemSelected.subscribe(onNext: { indexPath in
 // do something
}

跟我的需求是一样的。

于是就去看itemSelected的源代码:

/// Reactive wrapper for `delegate` message `collectionView(_:didSelectItemAtIndexPath:)`.
 public var itemSelected: ControlEvent<IndexPath> {
  let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didSelectItemAt:)))
   .map { a in
    return try castOrThrow(IndexPath.self, a[1])
   }

  return ControlEvent(events: source)
 }

souce是一个Observable,由delegate.methodInvoked产生。delegate是什么delegate?为什么会有methodInvoked方法?于是继续点进去。

extension Reactive where Base: UIScrollView {
   /// ...这部分代码省略不用看

  /// Reactive wrapper for `delegate`.
  ///
  /// For more information take a look at `DelegateProxyType` protocol documentation.
  public var delegate: DelegateProxy<UIScrollView, UIScrollViewDelegate> {
   return RxScrollViewDelegateProxy.proxy(for: base)
  }

  /// ...后面的代码暂时也不用看
}

可以看到delegate是一个DelegateProxy<UIScrollView, UIScrollViewDelegate>类型,根据字面是理解就是代理的代理。然后还看到这里的rx是扩展自UIScrollView的,UICollectionView是继承自UIScrollView,可以知道这里的delegate也是继承过来的使用的。还可以看到RxScrollViewDelegateProxy这个东西,可以想到如果我们要仿写的话,自己也应该写这样一个代理的代理类。先点进去看看:

open class RxScrollViewDelegateProxy
 : DelegateProxy<UIScrollView, UIScrollViewDelegate>
 , DelegateProxyType
 , UIScrollViewDelegate {

 /// Typed parent object.
 public weak private(set) var scrollView: UIScrollView?

 /// - parameter scrollView: Parent object for delegate proxy.
 public init(scrollView: ParentObject) {
  self.scrollView = scrollView
  super.init(parentObject: scrollView, delegateProxy: RxScrollViewDelegateProxy.self)
 }

 // Register known implementations
 public static func registerKnownImplementations() {
  self.register { RxScrollViewDelegateProxy(scrollView: $0) }
  self.register { RxTableViewDelegateProxy(tableView: $0) }
  self.register { RxCollectionViewDelegateProxy(collectionView: $0) }
  self.register { RxTextViewDelegateProxy(textView: $0) }
 }

 /// ...后面的感觉没什么关系,先不看
}

可以看到它其实是一个DelegateProxy<UIScrollView, UIScrollViewDelegate>,并且遵守了DelegateProxyType和UIScrollViewDelegate协议,可以感觉出它是一个链接rx和delegate的纽带。有一个实例变量scrollView,有一个init方法,有一个registerKnownImplementations静态方法。

现在脑海中大概有一个模糊的思路:我们要先创建一个纽带delegateProxy对象,然后在目标类的rx扩展中创建一个delegateProxy实例,最后在我们的assetsSelected事件流中用这个delegateProxy的methodInvoked截获delegate中的目标方法,并生成可订阅的Observable返回给controlEvent,这样链接打通。

开动

首先创建一个RxPhotoPickerControllerDelegateProxy

class RxPhotoPickerControllerDelegateProxy: DelegateProxy<ZZPhotoPickerController, ZZPhotoPickerControllerDelegate>, DelegateProxyType, ZZPhotoPickerControllerDelegate {

 /// Typed parent object.
 public weak private(set) var photoPickerController: ZZPhotoPickerController?

 /// - parameter scrollView: Parent object for delegate proxy.
 public init(photoPickerController: ParentObject) {
  self.photoPickerController = photoPickerController
  super.init(parentObject: photoPickerController, delegateProxy: RxPhotoPickerControllerDelegateProxy.self)
 }

 static func registerKnownImplementations() {
  self.register { RxPhotoPickerControllerDelegateProxy(photoPickerController: $0) }
 }

 // 把上面的写好后,编辑器会提示你需要实现一下两个方法,一个是获取,一个是设置,所以很好理解该在方法里实现什么。
 static func currentDelegate(for object: ZZPhotoPickerController) -> ZZPhotoPickerControllerDelegate? {
  return object.zzDelegate
 }

 static func setCurrentDelegate(_ delegate: ZZPhotoPickerControllerDelegate?, to object: ZZPhotoPickerController) {
  object.zzDelegate = delegate
 }

}

然后给目标的rx扩展写一个delegateProxy实例:

extension Reactive where Base: ZZPhotoPickerController {

 public var zzDelegate: DelegateProxy<ZZPhotoPickerController, ZZPhotoPickerControllerDelegate> {
  return RxPhotoPickerControllerDelegateProxy.proxy(for: base)
 }

}

最后写我们的assetsSelected:

extension Reactive where Base: ZZPhotoPickerController {

 var assetsSelected: ControlEvent<[Any]> {
  let source: Observable<[Any]> = self.zzDelegate.methodInvoked(#selector(ZZPhotoPickerControllerDelegate.photoPickerController(_:didSelect:))).map { a in
   return a[1] as! [Any]
  }
  return ControlEvent.init(events: source)
 }

}

要注意里面有个方法castOrThrow,这个方法rx并没有开放出来,是个内部方法,如果照着写报错。可以研究出该方法只是一个类型推断而已,所以可以简单写。

完成

然后就可以愉快的去对assetsSelected进行订阅了。

vc.rx.assetsSelected.subscribe(onNext: { (assets) in
    // do something
   }).disposed(by: self.disposeBag)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • RxSwift实现替换delegate的方法示例

    目标 最近写项目 ,写到需要为自己写的一个控件添加rx订阅方式的案例. 目前有一个代理: // 代理方式获取结果 @objc public protocol ZZPhotoPickerControllerDelegate : NSObjectProtocol { @objc optional func photoPickerController(_ photoPickerController: ZZPhotoPickerController, didSelect assets: [Any]) }

  • python同时替换多个字符串方法示例

    本文介绍了python同时替换多个字符串方法示例,分享给大家,具体如下: import re words = ''' 钟声响起归家的讯号 在他生命里 仿佛带点唏嘘 黑色肌肤给他的意义 是一生奉献 肤色斗争中 年月把拥有变做失去 疲倦的双眼带着期望 今天只有残留的躯壳 迎接光辉岁月 风雨中抱紧自由 一生经过彷徨的挣扎 自信可改变未来 问谁又能做到 可否不分肤色的界限 愿这土地里 不分你我高低 缤纷色彩闪出的美丽 是因它没有 分开每种色彩 年月把拥有变做失去 疲倦的双眼带着期望 今天只有残留的躯壳

  • 使用java8的方法引用替换硬编码的示例代码

    背景 想必大家在项目中都有遇到把一个列表的多个字段累加求和的情况,也就是一个列表的总计.有的童鞋问,这个不是给前端做的吗?后端不是只需要把列表返回就行了嘛...没错,我也是这样想的,但是在一场和前端的撕逼大战中败下阵来之后,这个东西就落在我身上了.当时由于工期原因,时间比较紧,也就不考虑效率和易用性了,只是满足当时的需求,就随便写了个方法统计求和.目前稍微闲下来了,就把原来的代码优化下.我们先来看一下原来的代码... 原代码 工具类 import org.apache.commons.lang3

  • Oracle数据行拆分多行方法示例

    工作和学习中常常会遇到一行要分割成多行数据的情况,在此整理一下做下对比. 单行拆分 如果表数据只有一行,则可以直接在原表上直接使用connect by+正则的方法,比如: select regexp_substr('444.555.666', '[^.]+', 1, level) col from dual connect by level <= regexp_count('444.555.666', '\.') + 1 输出结果: COL ---- 444 555 666 多行拆分 如果数据表

  • swift 3.0中实现字符串截取、比较的方法示例

    前言 字符串处理一直都是程序开发中不可避免的,而字符串截取/替换操作更是频繁.swift3.0 中不能直接使用下标数字进行字符串解决,只能使用String.Index来做位置索引,要想实现截取功能首先得获取到String.Index; 下面话不多说了,来一起看看详细的介绍吧. 实例代码 下面两段代码获取开头可结尾,获取中间部分参数用Range<Index>即可: 获取结尾两个字符子串: let sessionId = "this is a test" let index =

  • jQuery事件 delegate()使用方法介绍

    delegate定义和用法 delegate() 方法为指定的元素(属于被选元素的子元素)添加一个或多个事件处理程序,并规定当这些事件发生时运行的函数. 使用 delegate() 方法的事件处理程序适用于当前或未来的元素(比如由脚本创建的新元素). 参数 描述 childSelector 必需.规定要附加事件处理程序的一个或多个子元素. event 必需.规定附加到元素的一个或多个事件. 由空格分隔多个事件值.必须是有效的事件. data 可选.规定传递到函数的额外数据. function 必

  • JS简单获得节点元素的方法示例

    本文实例讲述了JS简单获得节点元素的方法.分享给大家供大家参考,具体如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>www.jb51.net - JS几种获得节点元素的方法</title> <script type="application/javascript">

  • 使用pandas对矢量化数据进行替换处理的方法

    使用pandas处理向量化的数据,进行数据的替换时不仅仅能够进行字符串的替换也能够处理数字. 做简单的示例如下: In [4]: data = Series(range(5)) In [5]: data Out[5]: 0 0 1 1 2 2 3 3 4 4 dtype: int64 In [6]: data.replace(3,333) Out[6]: 0 0 1 1 2 2 3 333 4 4 dtype: int64 In [7]: data Out[7]: 0 0 1 1 2 2 3 3

  • Go语言中的字符串处理方法示例详解

    1 概述 字符串,string,一串固定长度的字符连接起来的字符集合.Go语言的字符串是使用UTF-8编码的.UTF-8是Unicode的实现方式之一. Go语言原生支持字符串.使用双引号("")或反引号(``)定义. 双引号:"", 用于单行字符串. 反引号:``,用于定义多行字符串,内部会原样解析. 示例: // 单行 "心有猛虎,细嗅蔷薇" // 多行 ` 大风歌 大风起兮云飞扬. 威加海内兮归故乡. 安得猛士兮守四方! ` 字符串支持转义

  • 在Angular中使用JWT认证方法示例

    本文介绍了在Angular中使用JWT认证方法示例,分享给大家,具体如下: 项目地址: grading-system 基于session的认证和基于token的认证的方式已经被广泛使用.在session认证中,服务端会存储一份用户登录信息,这份登录信息会在响应时传递给浏览器并保存为Cookie,在下次请求时,会带上这份登录信息,这样就能识别请求来自哪个用户. 在基于session的认证中,每个用户都要生成一份session,这份session通常保存在内存中,随着用户量的增加,服务端的开销会增大

随机推荐