Last active
April 17, 2021 16:48
-
-
Save sledsworth/67528bfc04a630cf8d5a9147bb9c514d to your computer and use it in GitHub Desktop.
Generic Animated Progress Bar in SwiftUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import UIKit | |
import SwiftUI | |
import Combine | |
class NutrientModel: Progressable { | |
var willChange = PassthroughSubject<BaseNutrient, Never>() | |
var id = UUID.init() | |
var name: String | |
var description: String { | |
get { | |
return "\(current) / \(total)" | |
} | |
} | |
var current: CGFloat | |
var total: CGFloat | |
var progress: CGFloat { | |
get { | |
return current / total | |
} | |
} | |
var gradient: LinearGradient | |
init(name: String, unit: NutrientUnit, total: CGFloat, colors: [Color] = [Color(red: 239.0 / 255, green: 120.0 / 255, blue: 221.0 / 255), Color(red: 239.0 / 255, green: 172.0 / 255, blue: 120.0 / 255)], start: CGFloat = 0) { | |
self.name = name | |
self.total = total | |
self.current = start | |
self.gradient = LinearGradient(gradient: .init(colors: colors), startPoint: .init(x: 0.0, y: 0), | |
endPoint: .init(x: 1.0, y: 0)) | |
} | |
func addProgress(_ progress: CGFloat) { | |
willChange.send(self) | |
self.current += progress | |
} | |
func removeProgress(_ progress: CGFloat) { | |
willChange.send(self) | |
self.current -= progress | |
if self.current < 0 { | |
self.current = 0 | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
protocol Progressable: ObservableObject { | |
/// Name of the progressable field | |
var name: String { get } | |
/// The text form of the progress being made (ex. formatted percentage or fraction) | |
var description: String { get } | |
/// The percentage of progress made as float | |
var progress: CGFloat { get } | |
/// The linear gradient use to fill the completed portion of the bar | |
var gradient: LinearGradient { get set } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct ProgressBarComponent<P: Progressable> : View { | |
@ObservedObject var progression: P | |
var progressAnimation: Animation { | |
Animation | |
.interpolatingSpring(mass: 43, stiffness: 3.2, damping: 0.5) | |
.speed(3) | |
.delay(0.07) | |
} | |
var barHeight: CGFloat = 12.0 | |
var body: some View { | |
VStack { | |
HStack { | |
Text(self.progression.name) | |
.font(.subheadline) | |
.foregroundColor(.gray) | |
Spacer() | |
Text(self.progression.description) | |
.font(.subheadline) | |
.foregroundColor(.gray) | |
} | |
.padding(.bottom, -8) | |
.padding(.horizontal, 4) | |
ZStack { | |
GeometryReader { geometry in | |
RoundedRectangle(cornerRadius: 100.0) | |
.fill( | |
Color(.displayP3, | |
red: 150 / 255, | |
green: 150 / 255, | |
blue: 150 / 255, | |
opacity: 0.1) | |
) | |
.overlay( | |
RoundedRectangle(cornerRadius: 100.0) | |
.size( | |
width: max(geometry.size.width * self.progression.progress, self.barHeight), | |
height: geometry.size.height) | |
.fill(self.progression.gradient) | |
.animation(self.progressAnimation) | |
) | |
} | |
} | |
.frame(height: self.barHeight, alignment: .leading) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct DayProgressContainer : View { | |
var trackedNutreints: [NutrientModel] | |
var body: some View { | |
return VStack { | |
ForEach(self.trackedNutreints, id: \.self) { nutrient in | |
ProgressBarComponent(progression: nutrient) | |
.padding(.vertical, 4) | |
} | |
} | |
} | |
} |
Need to make the following changes for the lates version of SwiftUI.
Change in Progressable.swift
protocol Progressable: BindableObject {
To
protocol Progressable: ObservableObject {
Change in ProgressBarComponent.swift
@ObjectBinding var progression: P
To
@ObservedObject var progression: P
Thanks @mwcs01! I went ahead and updated a few other API changes (foregroundColor
) as well.
I have been struggling on how to use it. Any chance you could include how you used it to create one of your example views above?
@mwcs01 I added an example--I didn't have a chance to test it, but hopefully you get the idea? This code is a bit different in my app now, I'll try to update it sometime soon.
Thanks!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Examples