要RxSwift
import RxSwift
import UIKit
final class Indicator {
/// シングルトン
static let shared = Indicator()
/// モーダル用のView
private let modal = UIView()
/// インジケーター
private let indicator = UIActivityIndicatorView()
/// 非同期スケジューラ
private let backgroundScheduler = ConcurrentDispatchQueueScheduler(qos: .background)
/// 初期処理
private init() {
indicator.style = .whiteLarge
indicator.color = Theme.Colors.black
modal.addSubview(indicator)
setModalStyle()
}
/// モーダルウィンドウ風のデザインにする
private func setModalStyle() {
indicator.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
indicator.layer.cornerRadius = 15
indicator.backgroundColor = Theme.Colors.indicatorBackground
modal.backgroundColor = Theme.Colors.modalBackground
}
/// インジケーターを表示する
///
/// - Parameter target: インジケーター表示対象のView
func show(target: UIView? = UIApplication.topViewController()?.view) {
guard let target = target else {
return
}
target.addSubview(modal)
target.bringSubviewToFront(modal)
modal.frame = target.frame
indicator.center = modal.center
fadeIn { [weak self] in
self?.indicator.startAnimating()
}
}
/// インジケーターを非表示にする
func dismiss() {
fadeOut { [weak self] in
self?.indicator.stopAnimating()
self?.modal.removeFromSuperview()
}
}
/// フェードイン
///
/// - Parameter before: フェードイン前の処理
private func fadeIn(before: () -> Void) {
before()
modal.alpha = 0.0
UIView.animate(withDuration: 0.3, animations: { [weak self] in
self?.modal.alpha = 1.0
})
}
/// フェードアウト
///
/// - Parameter after: フェードアウト後の処理
private func fadeOut(after: @escaping () -> Void) {
modal.alpha = 1.0
UIView.animate(withDuration: 0.3, animations: { [weak self] in
self?.modal.alpha = 0.0
}, completion: { _ in
after()
})
}
/// 非同期タスクの作成
///
/// - Parameter target: インジケーター表示対象のView
/// - Parameter task: タスク
/// - Returns: 非同期タスク
func createAsyncTask<T>(target: UIView? = UIApplication.topViewController()?.view,
task: @escaping () throws -> T) -> Observable<T> {
let observable = Observable<T>.create { observer in
do {
observer.on(.next(try task()))
observer.on(.completed)
} catch {
observer.on(.error(error))
}
return Disposables.create()
}
return createAsyncTask(target: target, observable: observable)
}
/// 非同期タスクの作成
///
/// - Parameters:
/// - target: インジケーター表示対象のView
/// - observable: タスク
/// - Returns: 非同期タスク
func createAsyncTask<T>(target: UIView? = UIApplication.topViewController()?.view,
observable: Observable<T>) -> Observable<T> {
return observable
.subscribeOn(backgroundScheduler)
.observeOn(MainScheduler.instance)
.do(onSubscribe: { [weak self] in
self?.show(target: target)
}, onDispose: { [weak self] in
self?.dismiss()
})
}
}