Подробнее
Xcode 9.2, swift 4
Решение
class LoadingProcess {
let minValue: Int
let maxValue: Int
var currentValue: Int
private let progressQueue = DispatchQueue(label: "ProgressView")
private let semaphore = DispatchSemaphore(value: 1)
init (minValue: Int, maxValue: Int) {
self.minValue = minValue
self.currentValue = minValue
self.maxValue = maxValue
}
private func delay(stepDelayUsec: useconds_t, completion: @escaping ()->()) {
usleep(stepDelayUsec)
DispatchQueue.main.async {
completion()
}
}
func simulateLoading(toValue: Int, step: Int = 1, stepDelayUsec: useconds_t? = 10_000,
valueChanged: @escaping (_ currentValue: Int)->(),
completion: ((_ currentValue: Int)->())? = nil) {
semaphore.wait()
progressQueue.sync {
if currentValue <= toValue && currentValue <= maxValue {
usleep(stepDelayUsec!)
DispatchQueue.main.async {
valueChanged(self.currentValue)
self.currentValue += step
self.semaphore.signal()
self.simulateLoading(toValue: toValue, step: step, stepDelayUsec: stepDelayUsec, valueChanged: valueChanged, completion: completion)
}
} else {
self.semaphore.signal()
completion?(currentValue)
}
}
}
func finish(step: Int = 1, stepDelayUsec: useconds_t? = 10_000,
valueChanged: @escaping (_ currentValue: Int)->(),
completion: ((_ currentValue: Int)->())? = nil) {
simulateLoading(toValue: maxValue, step: step, stepDelayUsec: stepDelayUsec, valueChanged: valueChanged, completion: completion)
}
}
Использование
let loadingProcess = LoadingProcess(minValue: 0, maxValue: 100)
loadingProcess.simulateLoading(toValue: 80, valueChanged: { currentValue in
// Update views
})
DispatchQueue.global(qos: .background).async {
print("Start loading data")
sleep(5)
print("Data loaded")
loadingProcess.finish(valueChanged: { currentValue in
// Update views
}) { _ in
print("End")
}
}
Полный образец
Не забудьте добавить код решения здесь
import UIKit
class ViewController: UIViewController {
weak var counterLabel: UILabel!
weak var progressView: UIProgressView!
override func viewDidLoad() {
super.viewDidLoad()
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .fill
stackView.distribution = .fillProportionally
stackView.spacing = 8
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 80).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -80).isActive = true
let label = UILabel()
label.textAlignment = .center
label.text = "0"
label.font = UIFont.systemFont(ofSize: 46)
stackView.addArrangedSubview(label)
counterLabel = label
let progressView = UIProgressView()
progressView.trackTintColor = .lightGray
progressView.progressTintColor = .blue
progressView.layer.cornerRadius = 4
progressView.clipsToBounds = true
progressView.heightAnchor.constraint(equalToConstant: 8).isActive = true
stackView.addArrangedSubview(progressView)
self.progressView = progressView
let button = UIButton()
button.setTitle("Start", for: .normal)
button.addTarget(self, action: #selector(startButtonTapped), for: .touchUpInside)
button.setTitleColor(.blue, for: .normal)
button.heightAnchor.constraint(equalToConstant: 30).isActive = true
stackView.addArrangedSubview(button)
}
@objc func startButtonTapped() {
sample()
}
private func setProcess(currentValue: Int) {
let value = 0.01 * Float(currentValue)
self.counterLabel?.text = "\(currentValue)"
self.progressView?.setProgress(value, animated: true)
print("\(currentValue)")
}
func sample() {
let loadingProcess = LoadingProcess(minValue: 0, maxValue: 100)
loadingProcess.simulateLoading(toValue: 80, valueChanged: { currentValue in
self.setProcess(currentValue: currentValue)
})
DispatchQueue.global(qos: .background).async {
print("Start loading data")
sleep(5)
print("Data loaded")
loadingProcess.finish(valueChanged: { currentValue in
self.setProcess(currentValue: currentValue)
}) { _ in
print("end")
}
}
}
}
Результаты