'Half circular progress bar with gradient swift
I want to create a half circular progress bar with gradient colour in iOS using swift. The minimum value of progress bar is 300 and maximum value of progress bar is 900. The current value of the progress bar is dynamic. I am attaching screenshot ans css for colour reference. Can someone please provide me a small working demo?
below is the CSS -
/* Layout Properties */
top: 103px;
left: 105px;
width: 165px;
height: 108px;
/* UI Properties */
background: transparent linear-gradient
(269deg, #32E1A0 0%, #EEED56 23%, #EFBF39 50%, #E59148 75%, #ED4D4D 100%)
0% 0% no-repeat padding-box;
opacity: 1;
Solution 1:[1]
you can create not same but similar progress bar with framework called MKMagneticProgress
example code :-
import MKMagneticProgress
@IBOutlet weak var magProgress:MKMagneticProgress!
override func viewDidLoad() {
magProgress.setProgress(progress: 0.5, animated: true)
magProgress.progressShapeColor = UIColor.blue
magProgress.backgroundShapeColor = UIColor.yellow
magProgress.titleColor = UIColor.red
magProgress.percentColor = UIColor.black
magProgress.lineWidth = 10
magProgress.orientation = .top
magProgress.lineCap = .round
magProgress.title = "Title"
magProgress.percentLabelFormat = "%.2f%%"
}
i hope it will work ... :)
Solution 2:[2]
this could solve your problem in swiftui
import SwiftUI
struct ContentView: View {
@State var progressValue: Float = 0.3
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
@State private var degress: Double = -110
var body: some View {
VStack {
ZStack{
ProgressBar(progress: self.$progressValue)
.frame(width: 250.0, height: 250.0)
.padding(40.0).onReceive(timer) { _ in
withAnimation {
if progressValue < 0.8999996 {
progressValue += 0.0275
}
}
}
ProgressBarTriangle(progress: self.$progressValue).frame(width: 280.0, height: 290.0).rotationEffect(.degrees(degress), anchor: .bottom)
.offset(x: 0, y: -150).onReceive(timer) { input in
withAnimation(.linear(duration: 0.01).speed(200)) {
if degress < 110.0 {
degress += 10
}
print(degress)
}
}
}
Spacer()
}
}
struct ProgressBar: View {
@Binding var progress: Float
var body: some View {
ZStack {
Circle()
.trim(from: 0.3, to: 0.9)
.stroke(style: StrokeStyle(lineWidth: 12.0, lineCap: .round, lineJoin: .round))
.opacity(0.3)
.foregroundColor(Color.gray)
.rotationEffect(.degrees(54.5))
Circle()
.trim(from: 0.3, to: CGFloat(self.progress))
.stroke(style: StrokeStyle(lineWidth: 12.0, lineCap: .round, lineJoin: .round))
.fill(AngularGradient(gradient: Gradient(stops: [
.init(color: Color.init(hex: "ED4D4D"), location: 0.39000002),
.init(color: Color.init(hex: "E59148"), location: 0.48000002),
.init(color: Color.init(hex: "EFBF39"), location: 0.5999999),
.init(color: Color.init(hex: "EEED56"), location: 0.7199998),
.init(color: Color.init(hex: "32E1A0"), location: 0.8099997)]), center: .center))
.rotationEffect(.degrees(54.5))
VStack{
Text("824").font(Font.system(size: 44)).bold().foregroundColor(Color.init(hex: "314058"))
Text("Great Score!").bold().foregroundColor(Color.init(hex: "32E1A0"))
}
}
}
}
struct ProgressBarTriangle: View {
@Binding var progress: Float
var body: some View {
ZStack {
Image("triangle").resizable().frame(width: 10, height: 10, alignment: .center)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
extension Color {
init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int: UInt64 = 0
Scanner(string: hex).scanHexInt64(&int)
let a, r, g, b: UInt64
switch hex.count {
case 3: // RGB (12-bit)
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(a, r, g, b) = (1, 1, 1, 0)
}
self.init(
.sRGB,
red: Double(r) / 255,
green: Double(g) / 255,
blue: Double(b) / 255,
opacity: Double(a) / 255
)
}
}
Solution 3:[3]
I implemented something like your progress bar, used this for level up - gain exp. progress.,
if you go from 300 to 1000, total bar is 1500 progressFrom: 300/1500, progressTo: 1000/1500 ..
func progressAnimation(duration: TimeInterval, progressFrom: CGFloat, progressTo: CGFloat) {
if progressFrom == 0 {
baseProgressLayer.removeFromSuperlayer()
}
let circlePath = UIBezierPath(ovalIn: CGRect(center: CGPoint(x: 93.0, y: 93.0), size: CGSize(width: 178, height: 178)))
progressLayer.path = circlePath.cgPath
progressLayer.fillColor = UIColor.clear.cgColor
progressLayer.strokeColor = getColor(progress: progressTo).cgColor
progressLayer.lineWidth = 8.0
layer.addSublayer(progressLayer)
layer.transform = CATransform3DMakeRotation(CGFloat(90 * Double.pi / 180), 0, 0, -1)
let end = CABasicAnimation(keyPath: "strokeEnd")
end.fromValue = progressFrom
end.toValue = progressTo
end.duration = duration * 0.85
end.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
end.fillMode = CAMediaTimingFillMode.forwards
end.isRemovedOnCompletion = false
let color = CABasicAnimation(keyPath: "strokeColor")
color.fromValue = getColor(progress: progressFrom).cgColor
color.toValue = getColor(progress: progressTo).cgColor
color.duration = duration * 0.85
color.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
color.fillMode = CAMediaTimingFillMode.forwards
color.isRemovedOnCompletion = false
let group = CAAnimationGroup()
group.animations = [end, color]
group.duration = duration * 0.85
progressLayer.strokeStart = progressFrom
progressLayer.strokeEnd = progressTo
progressLayer.add(group, forKey: "move")
if progressFrom != 0 {
let color2 = CABasicAnimation(keyPath: "strokeColor")
color2.fromValue = getColor(progress: progressFrom).cgColor
color2.toValue = getColor(progress: progressTo).cgColor
color2.duration = duration * 0.85
color2.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
color2.fillMode = CAMediaTimingFillMode.forwards
color2.isRemovedOnCompletion = false
baseProgressLayer.add(color2, forKey: "baseColor")
}
}
there is a extension for getting color for progress
extension CircularProgressBar {
private func getColor (progress: CGFloat) -> UIColor{
if progress >= 0 && progress <= 0.5{
return UIColor(red: CGFloat(255 / 255), green: CGFloat((0+((108-0)*progress*2)) / 255), blue: 0, alpha: 1.0)
}
else if progress > 0.5 && progress <= 1.0 {
return UIColor(red: CGFloat((255-((255-126)*(progress-0.5)*2)) / 255), green: CGFloat((108+((211-108)*(progress-0.5)*2)) / 255), blue: CGFloat((0+((33-0)*(progress-0.5)*2)) / 255), alpha: 1.0)
}
else {
return UIColor.green
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Shivam Parmar |
| Solution 2 | |
| Solution 3 | babacan |



