[RxSwift] 使用RxSwift在畫面上呈現倒數功能

我們需要一個label來顯示目前的秒數,以及一個變數儲存我們做出來的倒數計時器

//倒數計時label
    @IBOutlet weak var timeCounterLabel: UILabel!
//存倒數計時的變數
    var timer:Disposable?

在viewModel建立timer秒數的變數,這邊我們設為60秒

class ViewModel{
//在這邊設定秒數
 let countDown = BehaviorRelay<Int>(value: 60)
}

將label與viewModel內變數綁定,達到顯示倒數的效果

 private func setupTimeCounterLabelBinding(){
        guard let viewModel = viewModel else { return  }
        viewModel.countDown.map({$0.toString()}).bind(to: timeCounterLabel.rx.text).disposed(by: disposeBag)
    }

這邊我們將timer的創建包成一個function,在viewDidLoad時執行
我們根據viewModel內的countDown變數決定倒數幾秒,並設定一個timer延遲0秒,每過1秒執行一次onNext,設定在主執行緒執行
當timer收到0時執行onCompleted,並且在onCompleted讓viewModel內變數接收到0,並且彈出一個alertView

private func setupTimerCountDown(){
        //根據viewModel內的countDown變數決定倒數幾秒
        guard let viewModel = viewModel else { return  }
        let count  = viewModel.countDown.value
        //設定一個timer延遲0秒,每過1秒執行一次onNext,設定在主執行緒
        timer = Observable<Int>.timer(.seconds(0), period: .seconds(1), scheduler: MainScheduler.instance)
            .map{return count - $0}//利用map轉換型態
            .take(until: {($0 == 0) })//當時間到0時停止
            .subscribe(onNext: { timePassed in
                guard let viewModel = self.viewModel else { return  }
                viewModel.countDown.accept(timePassed)
            }, onCompleted: {
            guard let viewModel = self.viewModel else { return  }
                viewModel.countDown.accept(0)//若沒設的話label會停在1秒
            //完成時跳出彈出式視窗
                self.showCustomAlertWithIcon(message: "時間到", buttonTitle: "確認"
                    , cancelAcction: { self.dismiss(animated: true, completion: nil)})
                //讓此頁消失
            })
        
}

在viewDidLoad時執行

override func viewDidLoad() {
        super.viewDidLoad()
        setupTimeCounterLabelBinding()
        setupTimerCountDown()
    }

最後則是在跳頁面之後要將timer回收。若沒做到這步的話會在背景繼續倒數。

override func viewWillDisappear(_ animated: Bool) {
        timer?.dispose()
    }