调用 .onNext(newArray) 时 RXSwift collectionView 不更新
RXSwift collectionView doesn't update when calling .onNext(newArray)
我遇到了问题。我得到了一个绑定到 winPinataActions PublishSubject<[Object]>() 的集合视图。最初,当加载 collectionview 时一切正常,它显示它对对象的所有内容,但是当拉动刷新操作更改 publishSubject 数据时 UI 没有更新,它仍然获取 PublishSubject 的旧内容。
这是我绑定 collectionView 的方式:
class WinPinatasViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
private let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
configureCollectionView()
}
func configureCollectionView() {
/..../
viewModel.winPinataActions
.observeOn(MainScheduler.instance)
.bind(to: collectionView.rx.items(cellIdentifier: "winPinatasCell", cellType: WinPinatasCell.self)) {(row, item, cell) in
cell.configureCell(with: item)
}.disposed(by: bag)
viewModel.getPinataActions()
}
@objc func handleRefreshControl() {
viewModel.getPinataActions()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.collectionView.refreshControl?.endRefreshing()
}
}
}
这是我的视图模型class:
class WinPinatasViewModel {
let winPinataActions = PublishSubject<[WinPinatasAction]>()
func getPinataActions() {
guard let ssoId = UserDefaultsStore.ssoId() else {
return
}
NetworkEngine.shared.gamificationNetwork.getUserWinPinataActions(subject: winPinataActions, ssoID: ssoId)
}
}
还有我的 NetworkEngine getuserPinataActions 方法:
func getUserWinPinataActions(subject winPinatasActions: PublishSubject<[WinPinatasAction]>, ssoID: String) {
//...//
let actions = try decoder.decode([WinPinatasAction].self, from: jsonData)
winPinatasActions.onNext(actions)
winPinatasActions.onCompleted()
//...//
}
拉动刷新操作完成后,将调用 handleRefreshControl() 方法。此外,在调试时,我可以看到在 pullToRefresh 操作之后,我的 NetworkEngine 方法中收到了新数据,并且 .onNext() 和 onCompleted() 都被调用了。但是当我滚动 collectionView 时,单元格项来自旧数组的数据,而不是一个新数组。请问你能帮帮我吗?我做错了什么?
这里的问题是您正在向 Subject 发送一个 completed
事件,但随后期望它能够在此之后发送其他事件。 Observable 契约规定,一旦 Observable(或本例中的 Subject)发送了一个完成的事件,它在任何情况下都不会再发送任何事件。
您应该从函数返回一个 Observable,而不是将 Subject 传递给 getUserWinPinataActions
。
这更接近你应该拥有的:
class WinPinatasViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
private let bag = DisposeBag()
let viewModel = WinPinatasViewModel()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.refreshControl!.rx.controlEvent(.valueChanged)
.startWith(())
.flatMapLatest { [viewModel] in
viewModel.getPinataActions()
}
.observeOn(MainScheduler.instance)
.bind(to: collectionView.rx.items(cellIdentifier: "winPinatasCell", cellType: WinPinatasCell.self)) {(row, item, cell) in
cell.configureCell(with: item)
}
.disposed(by: bag)
}
}
class WinPinatasViewModel {
func getPinataActions() -> Observable<[WinPinatasAction]> {
guard let ssoId = UserDefaultsStore.ssoId() else {
return .empty()
}
return GamificationNetwork.shared.getUserWinPinataActions(ssoID: ssoId)
}
}
class GamificationNetwork {
static let shared = GamificationNetwork()
func getUserWinPinataActions(ssoID: String) -> Observable<[WinPinatasAction]> {
Observable.create { observer in
let jsonData = Data() // get jsonData somehow
let actions = try! decoder.decode([WinPinatasAction].self, from: jsonData)
observer.onNext(actions)
observer.onCompleted()
return Disposables.create { /* cancelation code, if any */ }
}
}
}
记住:
Subjects provide a convenient way to poke around Rx, however they are not recommended for day to day use... In production code you may find that you rarely use the IObserver interface and subject types... The IObservable interface is the dominant type that you will be exposed to for representing a sequence of data in motion, and therefore will comprise the core concern for most of your work with Rx...
-- Intro to Rx
如果您发现自己伸手去拿 Subject 来解决问题,那么您可能做错了什么。
此外,这篇文章可能会有所帮助:Integrating RxSwift Into Your Brain and Code Base
我遇到了问题。我得到了一个绑定到 winPinataActions PublishSubject<[Object]>() 的集合视图。最初,当加载 collectionview 时一切正常,它显示它对对象的所有内容,但是当拉动刷新操作更改 publishSubject 数据时 UI 没有更新,它仍然获取 PublishSubject 的旧内容。 这是我绑定 collectionView 的方式:
class WinPinatasViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
private let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
configureCollectionView()
}
func configureCollectionView() {
/..../
viewModel.winPinataActions
.observeOn(MainScheduler.instance)
.bind(to: collectionView.rx.items(cellIdentifier: "winPinatasCell", cellType: WinPinatasCell.self)) {(row, item, cell) in
cell.configureCell(with: item)
}.disposed(by: bag)
viewModel.getPinataActions()
}
@objc func handleRefreshControl() {
viewModel.getPinataActions()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.collectionView.refreshControl?.endRefreshing()
}
}
}
这是我的视图模型class:
class WinPinatasViewModel {
let winPinataActions = PublishSubject<[WinPinatasAction]>()
func getPinataActions() {
guard let ssoId = UserDefaultsStore.ssoId() else {
return
}
NetworkEngine.shared.gamificationNetwork.getUserWinPinataActions(subject: winPinataActions, ssoID: ssoId)
}
}
还有我的 NetworkEngine getuserPinataActions 方法:
func getUserWinPinataActions(subject winPinatasActions: PublishSubject<[WinPinatasAction]>, ssoID: String) {
//...//
let actions = try decoder.decode([WinPinatasAction].self, from: jsonData)
winPinatasActions.onNext(actions)
winPinatasActions.onCompleted()
//...//
}
拉动刷新操作完成后,将调用 handleRefreshControl() 方法。此外,在调试时,我可以看到在 pullToRefresh 操作之后,我的 NetworkEngine 方法中收到了新数据,并且 .onNext() 和 onCompleted() 都被调用了。但是当我滚动 collectionView 时,单元格项来自旧数组的数据,而不是一个新数组。请问你能帮帮我吗?我做错了什么?
这里的问题是您正在向 Subject 发送一个 completed
事件,但随后期望它能够在此之后发送其他事件。 Observable 契约规定,一旦 Observable(或本例中的 Subject)发送了一个完成的事件,它在任何情况下都不会再发送任何事件。
您应该从函数返回一个 Observable,而不是将 Subject 传递给 getUserWinPinataActions
。
这更接近你应该拥有的:
class WinPinatasViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
private let bag = DisposeBag()
let viewModel = WinPinatasViewModel()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.refreshControl!.rx.controlEvent(.valueChanged)
.startWith(())
.flatMapLatest { [viewModel] in
viewModel.getPinataActions()
}
.observeOn(MainScheduler.instance)
.bind(to: collectionView.rx.items(cellIdentifier: "winPinatasCell", cellType: WinPinatasCell.self)) {(row, item, cell) in
cell.configureCell(with: item)
}
.disposed(by: bag)
}
}
class WinPinatasViewModel {
func getPinataActions() -> Observable<[WinPinatasAction]> {
guard let ssoId = UserDefaultsStore.ssoId() else {
return .empty()
}
return GamificationNetwork.shared.getUserWinPinataActions(ssoID: ssoId)
}
}
class GamificationNetwork {
static let shared = GamificationNetwork()
func getUserWinPinataActions(ssoID: String) -> Observable<[WinPinatasAction]> {
Observable.create { observer in
let jsonData = Data() // get jsonData somehow
let actions = try! decoder.decode([WinPinatasAction].self, from: jsonData)
observer.onNext(actions)
observer.onCompleted()
return Disposables.create { /* cancelation code, if any */ }
}
}
}
记住:
Subjects provide a convenient way to poke around Rx, however they are not recommended for day to day use... In production code you may find that you rarely use the IObserver interface and subject types... The IObservable interface is the dominant type that you will be exposed to for representing a sequence of data in motion, and therefore will comprise the core concern for most of your work with Rx...
-- Intro to Rx
如果您发现自己伸手去拿 Subject 来解决问题,那么您可能做错了什么。
此外,这篇文章可能会有所帮助:Integrating RxSwift Into Your Brain and Code Base