使用 RxSwift 刷新后绑定到 UIRefreshControl

Posted

技术标签:

【中文标题】使用 RxSwift 刷新后绑定到 UIRefreshControl【英文标题】:Binding to a UIRefreshControl after refresh using RxSwift 【发布时间】:2016-05-30 09:37:08 【问题描述】:

我有一个 TableView 的通知。我想使用 UIRefreshControl 拉刷新。如何使用 rx-swift 做到这一点?这是我的代码。为什么我将值设置为变量数据后tableView没有刷新

var refreshControl = UIRefreshControl()
var disposeBag = DisposeBag()

let loadingData = ActivityIndicator()

var data: Observable<[Notification]>!

override func viewDidLoad() 
    super.viewDidLoad()

    self.view = v

    v.tableView.registerClass(NotificationsViewCell.self, forCellReuseIdentifier: "Cell")
    v.tableView.addSubview(refreshControl)
    data = getNotifications()

    configureTableDataSource()
    configureActivityIndicatorsShow()

    refreshControl.rx_controlEvent(.ValueChanged)
        .flatMapLatest [unowned self] _ in
            return self.getNotifications()
            .trackActivity(self.loadingData)
        .subscribe(
            onNext: notification in
                print("success")
                self.data = Observable.just(notification) // NOT REFRESH TABLEVIEW
            ,
            onError:  error in
                print("Error \(error)")
            ,
            onCompleted: () in
                print("complete")
            ,
            onDisposed: () in
                print("disposed")
            )
        .addDisposableTo(disposeBag)



func configureTableDataSource()
    data
        .retry(3)
        .doOnError [weak self] error in
            self?.v.emptyLabel.hidden = false
            self?.v.retryButton.hidden = false
        
        .doOnNext [weak self] result in
            if result.count == 0 
                self?.v.emptyLabel.hidden = false
                self?.v.emptyLabel.text = "Tidak ada bisnis favorit"
             else 
                self?.v.emptyLabel.hidden = true
                self?.v.retryButton.hidden = true
            
        
        .trackActivity(loadingData)
        .retryWhen _ in
            self.v.retryButton.rx_tap
        
        .asDriver(onErrorJustReturn: [])
        .map results in
            results.map(NotificationsViewModel.init)
        
        .drive(v.tableView.rx_itemsWithCellIdentifier("Cell", cellType: NotificationsViewCell.self))  (index, viewModel, cell) in
            cell.viewModel = viewModel

            let tap = UITapGestureRecognizer(target: self, action: #selector(self.goToProfile(_:)))
            tap.numberOfTapsRequired = 1
            cell.photo.tag = index
            cell.photo.addGestureRecognizer(tap)
        
        .addDisposableTo(disposeBag)


func configureActivityIndicatorsShow()
    loadingData
        .driveNext isLoading in
            if !isLoading 
                self.v.indicatorView.stopAnimating()
             else 
                self.v.indicatorView.startAnimating()
                self.v.retryButton.hidden = true
                self.v.emptyLabel.hidden = true
            
        
        .addDisposableTo(disposeBag)

    loadingData.asObservable()
        .bindTo(refreshControl.rx_refreshing)
        .addDisposableTo(disposeBag)


func getNotifications() -> Observable<[Notification]> 
    let parameters = [
        "token": NSUserDefaults.standardUserDefaults().objectForKey("token")! as! String
    ]
    return string(.POST, NOTIFICATION_LIST, parameters: parameters)
        .map  json in
            return Notification.parseJSON(JSON.parse(json)["notifications"])
        
        .observeOn(MainScheduler.instance)

编辑::

var data = Variable<[Notification]>([])

override func viewDidLoad() 
    getNotifications()
        .retry(3)
        .doOnError [weak self] error in
            self?.v.emptyLabel.hidden = false
            self?.v.retryButton.hidden = false
        
        .doOnNext [weak self] result in
            if result.count == 0 
                self?.v.emptyLabel.hidden = false
                self?.v.emptyLabel.text = "Tidak ada notifikasi"
             else 
                self?.v.emptyLabel.hidden = true
                self?.v.retryButton.hidden = true
            
        
        .trackActivity(loadingData)
        .retryWhen _ in
            self.v.retryButton.rx_tap
        
        .bindTo(data)
        .addDisposableTo(disposeBag)


    refreshControl.rx_controlEvent(.ValueChanged)
        .flatMapLatest [unowned self] _ in
            return self.getNotifications()
                .doOnError [weak self] error in 
                    // This not call after the second pull to refresh if No network connection, so refresh control still appear
                    self?.refreshControl.endRefreshing()  
                
                .doOnCompleted [weak self] result in
                    self?.refreshControl.endRefreshing()
                
        .bindTo(data)
        .addDisposableTo(disposeBag)


func configureTableDataSource()
    datas.asObservable()
        .asDriver(onErrorJustReturn: [])
        .map results in
            results.map(NotificationsViewModel.init)
        
        .drive(v.tableView.rx_itemsWithCellIdentifier("Cell", cellType: NotificationsViewCell.self))  (index, viewModel, cell) in
            cell.viewModel = viewModel
        
        .addDisposableTo(disposeBag)



func configureActivityIndicatorsShow()
    loadingData
        .driveNext isLoading in
            if !isLoading 
                self.v.indicatorView.stopAnimating()
             else 
                self.v.indicatorView.startAnimating()
                self.v.retryButton.hidden = true
                self.v.emptyLabel.hidden = true
            
        
        .addDisposableTo(disposeBag)

【问题讨论】:

【参考方案1】:

self.data = Observable.just(notification) 正在创建一个新的Observable 并将新的[Notification] 元素发送到那个 Observable,没有人订阅。

您应该使用Subject,例如Variable

// instead of `var data: Observable<[Notification]>!`
let data = Variable<[Notification]>([])

// and then later, when you want to send out a new element:
self.data.value = notification

编辑:向您展示如何将它与您已有的东西结合使用。

// this will update `data` upon `refreshControl` value change
refreshControl.rx_controlEvent(.ValueChanged)
    .flatMapLatest [unowned self] _ in
        return self.getNotifications()
    
    .bindTo(data)
    .addDisposableTo(disposeBag)

// this will update `loadingData` when `data` gets a new element
data.asDriver().trackActivity(self.loadingData)

// bind to your table view
data.asDriver().drive(//.....

另外,考虑将retryretryWhen 移动得更快,而不是发生在您当前拥有它的下游(在表视图绑定中)。相反,我认为它应该属于getNotifications

【讨论】:

你的意思是“让数据=变量([])”?但是如何从这个“data = getNotifications()”中分配数据。我应该更改 getNotifications() 的返回类型吗? 好收获。我还更新了答案,向您展示您可能希望如何重新组织您的代码以符合我的建议。 谢谢,它的工作。我更新了我的代码。但是如果没有连接我有一点问题,然后我拉请求tableView。第一个动作 refreshControl 将隐藏,第二个动作它仍然出现。为什么?

以上是关于使用 RxSwift 刷新后绑定到 UIRefreshControl的主要内容,如果未能解决你的问题,请参考以下文章

UITextField 使用 RxSwift 绑定到 ViewModel

使用 RxSwift 将多个 UITextField 绑定到类道具

使用 RxSwift 将 Alamofire 请求绑定到表视图

RxSwift 使用 RxCocoa 绑定到动画

无法使用 RxSwift 变量 asObservable() 设置绑定(到:UITableView)

使用 RxSwift 将 UITableViewCell 中的控件绑定到 ViewModel 的最佳实践