美文网首页rxswiftiOS Dev
RxSwift 项目实战举例

RxSwift 项目实战举例

作者: AKyS佐毅 | 来源:发表于2017-08-06 12:34 被阅读555次
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.

相关文章

网友评论

  • 王博达:你好我想问一下 如何修复bindTo多次导致由tableviewcell重用 这段代码里面的
    sentMessage(#selector(Base.prepareForReuse))
    是怎么确定 prepareForReuse 方法是自己在 Reactive 的 extension 里写的方法而不是 cell 自带的?
    因为 base 指的是 cell 而不是 reactive

本文标题:RxSwift 项目实战举例

本文链接:https://www.haomeiwen.com/subject/rmeglxtx.html