RxSwift 项目实战举例

RxSwift.png

本文非基础文章,主要举例实际开发中可能会遇到的场景。

如何实现多按钮互斥单选操作

    //MARK: - 单选操作
    func rxRedio(){
        // force unwrap to avoid having to deal with optionals later on
        let buttons = [self.button1, self.button2, self.button3].map { $0! }
        
        // create an observable that will emit the last tapped button (which is
        // the one we want selected)
        let selectedButton = Observable.from(
            buttons.map { button in
                button.rx.tap.map { button }
            }
            ).merge()
        
        // for each button, create a subscription that will set its `isSelected`
        // state on or off if it is the one emmited by selectedButton
        buttons.reduce(Disposables.create()) { disposable, button in
            let subscription = selectedButton.map { $0 == button }
                .bind(to: button.rx.isSelected)
            
            // combine two disposable together so that we can simply call
            // .dispose() and the result of reduce if we want to stop all
            // subscriptions
            return Disposables.create(disposable, subscription)
            }
            .addDisposableTo(bag)
    }

如何实现数据的过滤操作

    
    func filterArray (){
        var allCites: Variable<[CiteModel?]> = Variable([])
        var searchQuery: Variable<String> = Variable("")
        
        let model1 = CiteModel()
        model1.cite = "131"
        
        let model2 = CiteModel()
        model2.cite = "331"
        
        let model3 = CiteModel()
        model3.cite = "121"
        
        let model4 = CiteModel()
        model4.cite = "1224"
        
        allCites.value = [model1, model2, model3]
        
        
        //通过combineLatest我们可以很容易实现两者任意一个改变都去改变输出结果的效果
        var shownCites: Observable<[CiteModel?]> = Observable.combineLatest(allCites.asObservable(), searchQuery.asObservable()) { allCites, query in
            allCites.filter { ($0?.cite.contains(query))! }
        }
        
        func filterCitesByQuery(query: String) {
            searchQuery.value = query
        }
        
        
        filterCitesByQuery(query: "2")
        
        shownCites.subscribe {  (event: Event<[CiteModel?]>) in
            //guard: 用来处理提前返回,给event重新赋值,如果event =nil,直接return。优点是防止代码嵌套过多
            guard event.element != nil else { return }
            
            for element in event.element! {
                print(element?.cite ?? "")
            }
            }.addDisposableTo(bag)
        
        
        allCites.value = [model1, model2, model3,model4]
        
        //通过Observable.from 将每一个元素都传递出来
        allCites.asObservable()
            .flatMap { CiteModel in
                Observable.from(CiteModel) // <- magic here
            }
            .subscribe(onNext: { item in
                print(item?.cite ?? "")
            })
            .addDisposableTo(bag)
    }

如何合并两个通知

    func rxNotification(){
        
        self.pushButton.rx.tap.subscribe {  (event: Event<()>) in
            NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: "121"), object: nil)
            }.addDisposableTo(bag)
        
        let a = NotificationCenter.default.rx.notification(NSNotification.Name.UIApplicationWillEnterForeground)
        let b = NotificationCenter.default.rx.notification(Notification.Name(rawValue: "121"))
        
        Observable.of(a, b)
            .merge()
            .takeUntil(self.rx.deallocated)
            .subscribe{ _ in
                print("如何合并两个通知")
            }.addDisposableTo(bag)
    }

如何监听某个方法执行 sentMessage 和 methodInvoked 只有一个区别,sentMessage 会在调用方法前发送值, methodInvoked 会在调用方法后发送值

     //方法一
     self.rx.sentMessage(#selector(UIViewController.viewWillAppear(_:)))
            .subscribe({ e in
                print(e)
            })
            .addDisposableTo(disposeBag)


     rx.methodInvoked(#selector(ViewController.viewWillAppear(_:)))
            .subscribe(onNext: { value in
               print("3")
           })
           .addDisposableTo(disposeBag)

另外,如果我们需要在一个控制中,监听另一个控制器是否执行了某个方法,那么该怎么处理呢?

import UIKit

class ListViewController: UIViewController {

    override  dynamic func viewDidLoad() {
        super.viewDidLoad()
         //do somethings 
    }

    override dynamic func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
       // do somethings 
    }
    
    dynamic func hello() {
        // do somethings 
    }
}

    let list = ListViewController()
        
    list.rx.sentMessage(#selector(ListViewController.hello)).subscribe(onNext: {
            print($0)
     }).addDisposableTo(bag)
        
    list.rx.sentMessage(#selector(ListViewController.viewDidAppear(_:))).subscribe(onNext: {
            print($0)
     }).addDisposableTo(bag)
        
    list.viewDidAppear(true)
    list.hello()

实际上,NSObject.rx.sentMessage和NSObject.rx.methodInvoked是基于方法swizzling,基于Swift方法调用的v表,它不受方法swizzling的影响。
如果要在swift中使用objc_msgSend方法,可以在函数上使用动态修饰符。
但在我看来,使用NSObject.rx.sentMessage和NSObject.rx.methodInvoked不是很好。

如何在长链(像a,b,c,d,e,...)中做一些Observable

        let a = PublishSubject<Int>()
        let b = PublishSubject<Int>()
        let c = PublishSubject<Int>()
        
        a.bind(to: b).addDisposableTo(bag)
        b.bind(to: c).addDisposableTo(bag)
        
        c.subscribe(onNext: {
            print($0) // called
        }).addDisposableTo(bag)
        
        
        let d = a.do(onNext: {
            print($0)
        })
        Observable.of(b, c, d).merge().subscribe(onNext: {
            print($0) // called
        }).addDisposableTo(bag)

        a.onNext(1)
        a.onNext(2)

UserDefaults KVO API 重复执行问题 Apple's Bug

import UIKit
import RxCocoa
import RxSwift
import RxDataSources


extension Reactive where Base: UITableView {
    
    var didHighlightRowAt: ControlEvent<IndexPath> {
        let selector = #selector(UITableViewDelegate.tableView(_:didHighlightRowAt:))
        let events = delegate
            .methodInvoked(selector)
            .filter({ ($0.last as? IndexPath) != nil })
            .map({ $0.last as! IndexPath })
        return ControlEvent(events: events)
    }
}


class RxTableViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!{
        didSet {
            tableView.register(UITableViewCell.self, forCellReuseIdentifier: String(describing: UITableViewCell.self))
            
            tableView.rx
                .itemSelected
                .subscribe { (indexPath) in
                    UserDefaults.standard.set("\(indexPath)", forKey: "key")
                   
                    self.navigationController?.pushViewController(RxSwiftLoginController(), animated: true)
                    
                }
                .disposed(by: disposeBag)
        }
    }
    
    let disposeBag = DisposeBag()

    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.rx.sentMessage(#selector(UIViewController.viewWillAppear(_:)))
            .subscribe({ e in
                print(e)
            })
            .addDisposableTo(disposeBag)
        
        
        UserDefaults.standard.rx
            .observe(String.self, "key")
            .debounce(0.1, scheduler: MainScheduler.asyncInstance) //iOS bug, v10.2  必须要加这句话
            .subscribe(onNext: { (value) in
                if let value = value {
                    print(value)
                }
            })
            .disposed(by: disposeBag)
        
        let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>()
        
        dataSource.configureCell = { (dataSource, tableView, indexPath, item) in
            let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: UITableViewCell.self), for: indexPath)
            cell.textLabel?.text = item
            
            return cell
        }
        
        Observable.just([SectionModel(model: "", items: (0..<5).map({ "\($0)" }))])
            .bind(to: tableView.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)
        
        
        let observable = UITableView().rx.didHighlightRowAt.asObservable()
        observable.subscribe{ (indexPath) in
            
        }.disposed(by: disposeBag)
        
        //通过设置dataSource.sections达到刷新tableView 的目的
        dataSource.setSections([SectionModel(model: "", items: (0..<10).map({ "\($0)" }))])


    }
}

如何防止多次点击事件,添加loading视图重复的问题

getClicksEvents()
.flatMapFirst(aVoid -> getRequestObservable())
.retry()
.subscribe( result -> //do your thing );

如何处理检查一些UIButton的isSelected状态,以提供另一个按钮的isEnabled状态。后一个按钮只有当其他其他按钮之一被选择为true时启用。

let state: Observable<Bool>

state.bindTo(button.rx.isSelected)
    .disposed(by: disposeBag)

state.bindTo(button1.rx.isEnabled)
    .disposed(by: disposeBag)

如何通过在RxSwift中组合它们来处理多个按钮的动作?

let tag1 = button1.rx.tap.map { [unowned self]  _ in return self.button1.tag}
let tag2 = button2.rx.tap.map { [unowned self]  _ in return self.button2.tag}
let tags = Observable.of(tag1, tag2).merge()

如何移除一个通知

  NotificationCenter.default.rx.notification(Notification.Name(rawValue: "121")).subscribe { _ in
        
   }.addDisposableTo(bag)

如何修复bindTo多次导致由tableviewcell重用?

import UIKit
import RxCocoa
import RxSwift

private var prepareForReuseBag: Int8 = 0

@objc public protocol Reusable : class {
    func prepareForReuse()
}

extension UITableViewCell: Reusable {}
extension UITableViewHeaderFooterView: Reusable {}
extension UICollectionReusableView: Reusable {}

extension Reactive where Base: Reusable {
    var prepareForReuse: Observable<Void> {
        return Observable.of(sentMessage(#selector(Base.prepareForReuse)).map { _ in }, deallocated).merge()
    }
    
    var reuseBag: DisposeBag {
        MainScheduler.ensureExecutingOnScheduler()
        
        if let bag = objc_getAssociatedObject(base, &prepareForReuseBag) as? DisposeBag {
            return bag
        }
        
        let bag = DisposeBag()
        objc_setAssociatedObject(base, &prepareForReuseBag, bag, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
        
        _ = sentMessage(#selector(Base.prepareForReuse))
            .subscribe(onNext: { [weak base] _ in
                let newBag = DisposeBag()
                objc_setAssociatedObject(base, &prepareForReuseBag, newBag, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
            })
        
        return bag
    }
}

或者

class TableViewCell: UITableViewCell {

   private(set) var disposeBag = DisposeBag()

   override func prepareForReuse() {
      super.prepareForReuse()
      disposeBag = DisposeBag() // because life cicle of every cell ends on prepare for reuse
   }
}


let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! DiaryItemCell

cell.commentButton.rx_tap
            .subscribeNext{
                showAlert("Hi")
            }.addDisposableTo(cell.disposeBag)

        return cell

如何实现筛选操作


    var shownCities = [String]() // Data source for UITableView
    let allCities = ["New York", "London", "Oslo", "Warsaw", "Berlin", "Praga"] // Our mocked API data source
    let disposeBag = DisposeBag() // Bag of disposables to release them when view is being deallocated

     searchBar
            .rx.text // Observable property thanks to RxCocoa
            .orEmpty // Make it non-optional
            .debounce(0.5, scheduler: MainScheduler.instance) // Wait 0.5 for changes.
            .distinctUntilChanged() // If they didn't occur, check if the new value is the same as old.
            .filter { !$0.isEmpty } // If the new value is really new, filter for non-empty query.
            .subscribe(onNext: { [unowned self] query in // Here we subscribe to every new value, that is not empty (thanks to filter above).
                self.shownCities = self.allCities.filter { $0.hasPrefix(query) } // We now do our "API Request" to find cities.
            })
            .addDisposableTo(disposeBag) // Don't forget to add this to disposeBag. We want to dispose it on deinit.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,690评论 0 9
  • 转载自:https://github.com/Tim9Liu9/TimLiu-iOS 目录 UI下拉刷新模糊效果A...
    袁俊亮技术博客阅读 11,915评论 9 105
  • 在生活中,有时候会看到这样一种人,他们总是游走在别人中间,给别人扮演着一种说客的角色。其实,这个说客的身份并不是每...
    云学科技阅读 5,155评论 0 1
  • 键盘消失除了以下这种 [searchBar resignFirstResponder]; 还有 -(void)se...
    杨大虾阅读 306评论 0 0