RxSwift - 如何显示进度条

9

在调用API时,我需要显示进度条并在调用完成后隐藏它。以下是我编写的用于填充表格的代码。在哪里应该调用以显示和隐藏API的进度?有没有使用RxSwift实现这个功能的方法?

items = fetchAllAnswers()
items.bindTo(self.myTableView.rx_itemsWithCellIdentifier("cellIdentifier", cellType: UITableViewCell.self)){ (row, element, cell) in
    cell.textLabel?.text = element
}
.addDisposableTo(disposeBag)

func fetchAllAnswers() -> Observable<[String]>{
    let api = Observable.create { (obsever: AnyObserver<[String]>) -> Disposable in
        //progress.show()
        let items = Api.getUsers()

        obsever.onNext(items)
        obsever.onCompleted()
        //progress.hide
        return AnonymousDisposable{
            print("api dispose called")
        }
    }
    return api
}
4个回答

8
你可以使用ActivityIndicator来自RxSwift repo。我在我的项目中使用MBProgressHUD。 首先,你需要为这个库创建扩展:
extension MBProgressHUD {

    /**
     Bindable sink for MBProgressHUD show/hide methods.
     */
    public var rx_mbprogresshud_animating: AnyObserver<Bool> {
        return AnyObserver { event in
            MainScheduler.ensureExecutingOnScheduler()

            switch (event) {
            case .Next(let value):
                if value {
                    let loadingNotification = MBProgressHUD.showHUDAddedTo(UIApplication.sharedApplication().keyWindow?.subviews.last, animated: true)
                    loadingNotification.mode = self.mode
                    loadingNotification.labelText = self.labelText
                    loadingNotification.dimBackground = self.dimBackground
                } else {
                    MBProgressHUD.hideHUDForView(UIApplication.sharedApplication().keyWindow?.subviews.last, animated: true)
                }
            case .Error(let error):
                let error = "Binding error to UI: \(error)"
                #if DEBUG
                    rxFatalError(error)
                #else
                    print(error)
                #endif
            case .Completed:
                break
            }
        }
    }
}

import RxSwift
import RxCocoa

extension Reactive where Base: MBProgressHUD {
    public var animation: Binder<Bool> {
        return Binder(self.base) { hud, show in
            guard let view = UIApplication.shared.keyWindow?.subviews.last()! else { return }
            if show {
                if hud.superview == nil {
                    view.addSubview(hud)
                }
                hud.show(animated: true)
            } else {
                hud.hide(animated: true)
            }
        }
    }
}

接下来,您需要在ViewController类中创建ActivityIndicator对象:
let progress = MBProgressHUD()
progress.mode = MBProgressHUDMode.Indeterminate
progress.labelText = "Loading..."
progress.dimBackground = true

let indicator = ActivityIndicator()
indicator.asObservable()
    .bindTo(progress.rx_mbprogresshud_animating)
    .addDisposableTo(bag)

接下来只需要将 trackActivity() 函数用于您的序列中:

apiMethod
.trackActivity(indicator)
.subscribeNext { stringArray in 
    items.value = stringArray
}
.addDisposableTo(bag)

通过在 'viewDidLoad' 中绑定到 'ProgressHUD',它开始显示。在调用 'trackActivity' 之前。 - Hamdullah shah
当调用了 'trackActivity' 方法之后,'MBProgressHUD' 才开始显示。我在所有项目中都使用它。如果在调用 'trackActivity' 之前就开始显示 'MBProgressHUD',可能是因为你做错了什么。你可以查看我的 RxSwift 教程 https://github.com/svyatoslav-reshetnikov/ReactiveApp,学习如何实现它。 - Svyatoslav

1

swift 4.2 && swift 5

// MBProgressHUD+Rx.swift
import RxSwift
import RxCocoa

extension Reactive where Base: MBProgressHUD {
    public var animation: Binder<Bool> {
        return Binder(self.base) { hud, show in
            guard let view = UIApplication.shared.keyWindow else { return }
            if show {
                if hud.superview == nil {
                    view.addSubview(hud)
                }
                hud.show(animated: true)
            } else {
                hud.hide(animated: true)
            }
        }
    }
}

使用

// hud init
private var hud: MBProgressHUD = {
    let progress = MBProgressHUD()
    progress.mode = MBProgressHUDMode.indeterminate
    progress.label.text = ""
    progress.backgroundColor = UIColor.black.withAlphaComponent(0.5)
    return progress
}()
// track
some_action.flatMapLatest{
    return some_operation1.trackActivity(hudModeIndicator)
}.flatMapLatest{
    return some_operation2.trackActivity(hudModeIndicator)
}.drive(onNext: {}).disposed(by: rx.disposeBag)

// bind
hudModeIndicator
    .drive(self.hud.rx.animation).disposed(by: rx.disposeBag)

参考资料

ActivityIndicator.swift


1

您需要在ViewController中完成这项工作,类似于以下内容:

var disposeBag = DisposeBag()

...

items = fetchAllAnswers()
      .subscribeOn(backgroundWorkScheduler)
      .observeOn(mainScheduler)
      .subscribe(
                onNext: { data in
                    print("onNext")
                    //show/update progress
                },
                onCompleted: {
                    print("onCompleted")
                    //hide progress
                },
                onDisposed: {
                    print("onDisposed")
                }
       ).addDisposableTo(disposeBag)

1

Swift 4

extension MBProgressHUD {
var rx_mbprogresshud_animating: AnyObserver<Bool> {

    return AnyObserver { event in

        MainScheduler.ensureExecutingOnScheduler()

        switch (event) {
        case .next(let value):
            if value {
                let loadingNotification = MBProgressHUD.showAdded(to: (UIApplication.shared.keyWindow?.subviews.last)!, animated: true)
                loadingNotification.mode = self.mode
                loadingNotification.label.text = self.label.text
            } else {
                MBProgressHUD.hide(for: (UIApplication.shared.keyWindow?.subviews.last)!, animated: true)
            }
        case .error(let error):
            let error = "Binding error to UI: \(error)"
            print(error)
        case .completed:
            break
        }
      }
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接