How can I build a progress bar with multiple progress in ios ? I don't think it is possible with the UIProgressView
I am looking for something like this(UI only) : Thanks !
The standard UIProgressView
shows only one progress and if you're trying to capture the progress of multiple items, you will have a hierarchy of Progress
/NSProgress
objects. E.g. if uploading five files, you have five Progress
objects, one for each upload, and then have a sixth NSProgress
, which the UIProgressView
observes, which has the other five as children. See Creating a Tree of Progress Objects in the NSProgress class reference. See https://stackoverflow.com/a/36616538/1271826 for an example.
If you want a progress view that visually represents the respective progress of individual NSProgress
, you'll have to do this yourself (or find a third party control that does this). But the standard UIProgressView
will not do this for you.
But it's not hard. You can, for example, create a horizontal stack view, add one UIProgressView
for each NSProgress
that you want to track, set the respective widths of the UIProgressView
subviews to be, for example, whatever percentage they represent of the overall tasks.
E.g. here is a horizontal stack view with three UIProgressView
arranged subviews:
Having said that, I prefer the standard single, consolidated UIProgessView
because the user can more easily see how far along they are. If you have a series of progress views like this, it's really hard to visually see "how much longer do I have" because you have to look at all the individual progress views and mentally figure it out (e.g. "the first looks like it's 50% done, the second is 66% done, the third is 75% done ... so exactly how far along is the whole thing?!?").
Anyway, here my Swift code to render the above. I know you're looking for Objective-C, but hopefully this is enough to illustrate the basic idea that can easily be implemented in either Swift or Objective-C:
class MultiProgressView: UIStackView {
struct ProgressInfo {
let progressView: UIProgressView
let totalToken: NSKeyValueObservation
let progressToken: NSKeyValueObservation
}
var progressInfo = [ProgressInfo]()
var progressHandler: ((Double) -> Void)?
var widthConstraints: [NSLayoutConstraint]?
func add(_ progress: Progress, progressTintColor: UIColor = .blue, trackTintColor: UIColor? = nil) {
let progressView = UIProgressView(progressViewStyle: .bar)
progressView.translatesAutoresizingMaskIntoConstraints = false
progressView.progressTintColor = progressTintColor
progressView.trackTintColor = trackTintColor ?? progressTintColor.withAlphaComponent(0.5)
addArrangedSubview(progressView)
progressView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 1)
progressView.observedProgress = progress
let totalToken = progress.observe(\.totalUnitCount) { [weak self] (_, _) in
self?.updateWidths()
}
let progressToken = progress.observe(\.completedUnitCount) { [weak self] (_, _) in
if let percent = self?.percent {
self?.progressHandler?(percent)
}
}
progressInfo.append(ProgressInfo(progressView: progressView, totalToken: totalToken, progressToken: progressToken))
updateWidths()
}
private func updateWidths() {
if let widthConstraints = self.widthConstraints {
removeConstraints(widthConstraints)
}
let totalUnitCount = self.totalUnitCount
guard totalUnitCount > 0 else { return }
widthConstraints = progressInfo.map { progressInfo in
let unitCount = progressInfo.progressView.observedProgress?.totalUnitCount ?? 0
return NSLayoutConstraint(item: progressInfo.progressView, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: CGFloat(unitCount) / CGFloat(totalUnitCount), constant: 0)
}
DispatchQueue.main.async {
self.addConstraints(self.widthConstraints!)
}
}
var totalUnitCount: Int64 {
return progressInfo.reduce(Int64(0)) { $0 + ($1.progressView.observedProgress?.totalUnitCount ?? 0) }
}
var completedUnitCount: Int64 {
return progressInfo.reduce(Int64(0)) { $0 + ($1.progressView.observedProgress?.completedUnitCount ?? 0) }
}
var percent: Double {
return Double(completedUnitCount) / Double(totalUnitCount)
}
}
And then to use it:
override func viewDidLoad() {
super.viewDidLoad()
let download1 = Download(...)
let download2 = Download(...)
let download3 = Download(...)
mainProgressView.progressHandler = { [weak self] percent in
self?.checkIfDone(percent: percent)
}
mainProgressView.add(download1.progress, progressTintColor: .blue)
mainProgressView.add(download2.progress, progressTintColor: .green)
mainProgressView.add(download3.progress, progressTintColor: .red)
download1.startDownload()
download2.startDownload()
download3.startDownload()
}
private func checkIfDone(percent: Double) {
if percent == 1 {
print("done")
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With