本文非基础文章,主要举例实际开发中可能会遇到的场景。
如何实现多按钮互斥单选操作
//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.