RxSwift+MVVM项目实战-多分组UITableView+RxDataSources+MJRefresh的使用

简介: RxSwift+MVVM项目实战-多分组UITableView+RxDataSources+MJRefresh的使用

1. 简介

对于MJRefresh的使用,大家都已不再陌生,它提供给我们的回调方式一种是Target-Action,另外一种是结合Block,使用的时候例如下面这样:

tableView.mj_header = MJRefreshNormalHeader(refreshingBlock: { ... })
tableView.mj_header = MJRefreshNormalHeader(refreshingTarget: self, refreshingAction: #selector(...))

mj_header的类型为MJRefreshHeadermj_footer的类型为MJRefreshFooter,两者均继承自MJRefreshComponent,通过源码可以看到,两者的正在刷新回调为以下两种方式:

/** 正在刷新的回调 */
@property (copy, nonatomic, nullable) MJRefreshComponentAction refreshingBlock;
/** 设置回调对象和回调方法 */
- (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action;

那么我们怎么把这两种方式更加“RxSwift”化一些?比如我们怎么这样调用tableView.rx.refreshing?下面我们分别看一下上面两种方式如何“RxSwift”化;


2. button.rx.tap的实现

我们想让通过Target-Action这种方式来响应事件,这个时候我们可以结合UIButton的点击事件来看“RxSwift”化是怎么做的?我们监听UIButton的点击可以这样做:


btn.rx.tap.subscribe { (_) in }

再来看一下tap属性,为ControlEvent类型,并且为ReactiveUIButton添加的扩展属性;

extension Reactive where Base: UIButton {
    /// Reactive wrapper for `TouchUpInside` control event.
    public var tap: ControlEvent<Void> {
        controlEvent(.touchUpInside)
    }
}

内部调用了函数,并返回了一个ControlEventControlEvent专门用于描述UI控件所产生的事件;


public func controlEvent(_ controlEvents: UIControl.Event) -> ControlEvent<()> { ... }

controlEvent函数内部创建了Observable,并且它的生命周期跟随UIControl一起被释放,返回值为ControlEvent类型,ControlEvent内部保存了Observable,并且subscribe(on: ConcurrentMainScheduler.instance)保证了构建函数只能在主线程进行,而ControlEvent内部提供的也有可被订阅的函数subscribe,故外界可以监听到Observable内部发出的信号;



public func controlEvent(_ controlEvents: UIControl.Event) -> ControlEvent<()> {
        let source: Observable<Void> = Observable.create { [weak control = self.base] observer in
                MainScheduler.ensureRunningOnMainThread()
                guard let control = control else {
                    observer.on(.completed)
                    return Disposables.create()
                }
                let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { _ in
                    observer.on(.next(()))
                }
                return Disposables.create(with: controlTarget.dispose)
            }
            .take(until: deallocated)
        return ControlEvent(events: source)
    }

再来看ControlTarget, 继承自RxTargetRxTarget继承自NSObject并遵守协议Disposable,目的是为了实现dispose方法,RxTarget的作用主要是为了延迟释放,它的生命周期跟随当调用dispose方法后,如果外界没有强引用RxTarget,则会被释放掉;在ControlTarget内部,保存了ControlEvent,并把事件通过Callback回调给外界,相当于把target-action的回调方法使用闭包在构造函数内部封装了一下,可以通过闭包去直接监听,并且他的生命周期为调用dispose函数后;

class RxTarget : NSObject
               , Disposable {
    private var retainSelf: RxTarget?
    override init() {
        super.init()
        self.retainSelf = self
    }
    func dispose() {
        self.retainSelf = nil
    }
}


final class ControlTarget: RxTarget {
    typealias Callback = (Control) -> Void
    let selector: Selector = #selector(ControlTarget.eventHandler(_:))
    weak var control: Control?
    let controlEvents: UIControl.Event
    var callback: Callback?
    init(control: Control, controlEvents: UIControl.Event, callback: @escaping Callback) {
        MainScheduler.ensureRunningOnMainThread()
        self.control = control
        self.controlEvents = controlEvents
        self.callback = callback
        super.init()
        control.addTarget(self, action: selector, for: controlEvents)
        let method = self.method(for: selector)
        ...
    }
    @objc func eventHandler(_ sender: Control!) {
        if let callback = self.callback, let control = self.control {
            callback(control)
        }
    }
    override func dispose() {
        super.dispose()
        self.control?.removeTarget(self, action: self.selector, for: self.controlEvents)
        self.callback = nil
    }
}

这个时候再看看controlEvent内部,就可以很清楚的知道当按钮点击的时候,observer会发出一个next事件,从而外界通过controlEvent可以订阅到这个事件,至此整个流程结束;

let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { _ in
    observer.on(.next(()))
}

3. 扩展MJRefresh的Target-Action的刷新机制实现

那么MJRefreshTarget-Action方式怎么可以实现上述这种方式呢?我们就比照着上面来就好了,首先我们想通过tableView.rx.xx的形式访问,必须给Rective做扩展,那么我们就扩展Rective并且为我们上述分析的MJRefreshComponent来做扩展,上述的RxTarget是不能被其他模块访问的,并且ControlTarget使用final修饰,不能被外界所继承,相当于我们就不能直接使用它了,那么我们就自己来搞一下上述的这两个类的实现:


typealias RefreshEvent = () -> Void
//避免因局部创建的时候,使用之后就释放,这里要手动控制他的释放时机
class LTTarget: NSObject, Disposable {
    private var retainSelf: LTTarget?
    override init() {
        super.init()
        self.retainSelf = self
    }
    func dispose() {
        self.retainSelf = nil
    }
    deinit {
        Logger("LTTarget deinit")
    }
}
//继承LTTarget,避免因局部创建的时候,使用之后就释放,这里要手动控制他的释放时机
final class RefreshTarget<Element: MJRefreshComponent>: LTTarget {
    private var element: Element //MJRefreshComponent
    private var event: RefreshEvent //刷新事件
    init(_ element: Element, event: @escaping RefreshEvent) {
        self.element = element
        self.event = event
        super.init()
        //监听事件
        element.setRefreshingTarget(self, refreshingAction: #selector(startRefresh))
    }
    @objc private func startRefresh() {
        self.event() //回调给外界
    }
    deinit {
        Logger("RefreshTarget deinit")
    }
}
extension Reactive where Base: MJRefreshComponent {
    var glt_refreshing: ControlEvent<Void> {
        let observable = Observable<Void>.create {[weak comp = self.base] (observer) -> Disposable in
            //保证在主线程执行 否则抛出异常
            MainScheduler.ensureExecutingOnScheduler()
            guard let _comp = comp else {
                observer.on(.completed)
                return Disposables.create()
            }
            //用于处理监听MJRefresh的正在刷新事件,RefreshTarget释放时机跟随引用着glt_refreshing的变量一起
            let disposeable = RefreshTarget(_comp) {
                observer.onNext(())
            }
            return disposeable
        }.take(until: deallocated)
        return ControlEvent(events: observable)
    }
}

那么我们以后就可以这样直接使用了:

tableView.mj_header?.rx.glt_refreshing.subscribe(onNext: { (_) in })
tableView.mj_footer?.rx.glt_refreshing.subscribe(onNext: { (_) in }


4. 扩展MJRefresh的Block的刷新机制实现

因为直接可以使用block调用,所以不用再抽象一个类和Target去做了;

extension Reactive where Base: MJRefreshComponent {
    var glt_block_refreshing: ControlEvent<Void> {
        let observable = Observable<Void>.create {[weak comp = self.base] (observer) -> Disposable in
            //保证在主线程执行 否则抛出异常
            MainScheduler.ensureExecutingOnScheduler()
            guard let _comp = comp else {
                observer.on(.completed)
                return Disposables.create()
            }
            //用于处理监听MJRefresh的正在刷新事件,RefreshTarget释放时机跟随引用着glt_refreshing的变量一起
            _comp.refreshingBlock = {
                observer.onNext(())
            }
            return Disposables.create()
        }.take(until: deallocated)
        return ControlEvent(events: observable)
    }
}

经过测试MJRefreshComponent以及RefreshTargetLTTarget都是可以正常释放的,具体的使用方式会在下一篇中介绍;

5. MJRefresh的header和footer的结束刷新

直接利用ReactiveUIScrollView做扩展,并且告诉结束的两种状态,endRefreshing代表结束刷新,noMoreData代表结束刷新并且没有更多数据了;在实际开发中,我们一般会无论收到任何一个的结束刷新操作,我们会同时调用headerfooter的结束刷新状态,这样可以避免掉一些意外的Bug,并且如果是footer的话,会区分还有没有更多数据,所以才这样设计;另外因为mj_headermj_footerOptional类型,保证了即使为空的时候,程序也不会Crash

enum LTRefreshEndState {
    case endRefreshing
    case noMoreData
}
extension Reactive where Base: UIScrollView {
    //用于外界刷新绑定、内部判断是是否有更多数据的UI状态
    var endRefreshing: Binder<LTRefreshEndState> {
        Binder<LTRefreshEndState>(self.base) { scrollView, state in
            scrollView.mj_header?.endRefreshing()
            if state == .noMoreData {
                scrollView.mj_footer?.endRefreshingWithNoMoreData()
            }else {
                scrollView.mj_footer?.endRefreshing()
            }
        }
    }
}

关于MJRefresh+RxSwift的实际使用会在下一篇中具体介绍;


复制搜一搜分享收藏划线


相关文章
|
6天前
|
JavaScript 小程序 Android开发
【经验分享】如何实现自定义的actionSheet组件
【经验分享】如何实现自定义的actionSheet组件
58 6
|
8月前
|
存储 数据安全/隐私保护
RxSwift+MVVM项目实战-登录功能实现
RxSwift+MVVM项目实战-登录功能实现
151 0
|
8月前
|
JSON HandyJSON Swift
RxSwift+MVVM项目实战-多分组TableView+MJRefresh+RxAlamofire+HandyJSON的使用
RxSwift+MVVM项目实战-多分组TableView+MJRefresh+RxAlamofire+HandyJSON的使用
221 0
|
8月前
RxSwift+MVVM项目实战-单分组UITableView的使用
RxSwift+MVVM项目实战-单分组UITableView的使用
179 0
|
8月前
|
存储 Swift
RxSwift+MVVM项目实战-多分组UITableView结合RxDataSources的使用
RxSwift+MVVM项目实战-多分组UITableView结合RxDataSources的使用
253 0
|
8月前
|
存储
RxSwift+MVVM项目实战-单分组UITableView添加、删除、移动功能
RxSwift+MVVM项目实战-单分组UITableView添加、删除、移动功能
74 0
|
8月前
|
API Swift iOS开发
|
iOS开发 开发者
iOS开发-简述UITableView中cell的重用问题
iOS开发-简述UITableView中cell的重用问题
166 0
|
Perl 测试技术 Android开发
RxSwift、RxBlocking、RxTest初尝试
在Android开发上,RxJava已经是非常重要的组成之一。最近在用Swift开发iOS应用,考虑在架构设计上使用RxSwift。 添加依赖 # Podfile use_frameworks! target 'YOUR_TARGET_NAME' do pod 'RxSwift', '~...
1719 0
|
前端开发
[译] 实用的 MVVM 和 RxSwift
今天我们将使用 RxSwift 实现 MVVM 设计模式。对于那些刚接触 RxSwift 的人,我 在这里 专门做了一个部分来介绍。
1321 0