调用 .onNext(newArray) 时 RXSwift collectionView 不更新

Posted

技术标签:

【中文标题】调用 .onNext(newArray) 时 RXSwift collectionView 不更新【英文标题】:RXSwift collectionView doesn't update when calling .onNext(newArray) 【发布时间】:2021-03-16 00:17:11 【问题描述】:

我遇到了问题。我有一个绑定到 winPinataActions PublishSubject() 的集合视图。最初,当加载collectionview 时一切正常,它显示为对象,但是当pull to refresh 操作更改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()
   


这是我的 viewModel 类:

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 数据时,单元格项来自旧数组,而不是一个新数组。请问你能帮帮我吗?我做错了什么?

【问题讨论】:

您的意思是在您的视图模型和网络引擎中都有一个winPinatasActions?尝试发布可编译的代码,我可能会提供帮助。 @DanielT。是的,winPinatasActions 是一个 PublishSubject() 并且当进行网络调用时 getUserWinPinataActions(subject winPinatasActions: PublishSubject, ssoID: String) 它作为参数发送并在获取我的网络调用后我使用 winPinatasActions.onNext(actions) 和 winPinatasActions.onCompleted() 发送操作。这些课程非常大,这就是我不发布整个课程的原因。但是我发布了与 collectionView、PublishSubject 和网络调用相关的所有内容。 不要发布整个课程。但是,如果您 do 发布的内容至少无需任何额外工作即可编译,这会有所帮助。请注意,我的答案中的代码也不是整个类,但它可以编译。 【参考方案1】:

这里的问题是您正在向主题发送一个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 提供了一种在 Rx 周围探索的便捷方式,但是不建议将它们用于日常使用...在生产代码中,您可能会发现您很少使用 IObserver 接口和主题类型...IObservable 接口是您将接触到的主要类型,用于表示运动中的数据序列,因此将构成您使用 Rx 的大部分工作的核心关注点...

-- Intro to Rx

如果您发现自己正在寻找主题来解决问题,那么您可能做错了什么。

另外,这篇文章可能会有所帮助:Integrating RxSwift Into Your Brain and Code Base

【讨论】:

以上是关于调用 .onNext(newArray) 时 RXSwift collectionView 不更新的主要内容,如果未能解决你的问题,请参考以下文章

Rx Swift 丢弃 .do(onNext: ) 我如何让它触发?

处理 Observable.subscribe 的结果时未调用 OnNext

RxJava2.1.0:在不同线程上订阅时未调用 PublishSubject onNext

捕获订阅 OnNext 操作可能引发的异常

RxSwift 调用 bind 立即触发 vs subscribe(onNext: )

Rx 操作符六