As per the image, Arrow-based works but now how to add one more white circle over gauge View and move the same as a white arrow.
Click here for Project
First of all, to reproduce the Indicator of the gauge I would suggest you to use a Shape like this one:
struct IndicatorShape: Shape {
func path(in rect: CGRect) -> Path {
return Path { path in
let width = rect.width
let height = rect.height
path.move(to: CGPoint(x: width / 2, y: 0))
path.addLine(to: CGPoint(x: 0, y: height))
path.addLine(to: CGPoint(x: width, y: height))
}
}
}
And then use it in your code as any other View. To add a Circle over your indicator you could use a simple overlay. Also, you need a masking effect to show the advancement of your gauge. It's a bit complicated and require some fine tuning... Here's the code:
struct MeterReadiness : View {
//speedometerBgColor
let colors = [Color.red,Color.yellow,Color.green,Color.blue]
@State var progress : CGFloat = 0.0
var body: some View{
GeometryReader {
let size = $0.size
ZStack{
Circle()
.trim(from: 0.0, to: 0.5)
.stroke(Color.red, lineWidth: 10)
.frame(width: 280, height: 280)
Circle()
.trim(from: 0, to: self.setProgress())
.stroke(AngularGradient(gradient: .init(colors: self.colors), center: .center, angle: .init(degrees: 180)), lineWidth: 10)
.frame(width: 280, height: 280)
ZStack{
Circle()
.trim(from: 0.0, to: 0.5)
.stroke(Color.blue, lineWidth: 10)
.frame(width: 280, height: 280)
Circle()
.trim(from: 0, to: self.setProgress())
.stroke(AngularGradient(gradient: .init(colors: self.colors), center: .center, angle: .init(degrees: 180)), lineWidth: 10)
.frame(width: 280, height: 280)
}
/// Mask the red gauge with the blue one
.mask {
/// To make the blue advance
Circle()
.trim(from: 0, to: (progress / 2) + 0.002)
.stroke(.white, lineWidth: 40)
}
}
.rotationEffect(.init(degrees: 180))
.overlay {
/// Custom Indicator
IndicatorShape()
.fill(.black)
.overlay(alignment: .bottom) {
/// Circle at the base of the Indicator
Circle()
.fill(.purple)
.frame(width: 30, height: 30)
.offset(y: 10)
} //: Overlay Indicator Bottom Dot
.frame(width: 25, height: 110)
.padding(.top, 40)
.offset(y: -5)
.overlay(alignment: .top) {
/// Here is the Circle above the Indicator
Circle()
.fill(.white)
.frame(width: 20, height: 20)
.offset(y: 0)
.shadow(radius: 1)
}
/// These two lines are to make the Indicator and the Circle move
.rotationEffect(.degrees(-90), anchor: .bottom)
.rotationEffect(.degrees(progress * 180), anchor: .bottom)
.offset(y: -75)
} //: Overlay Indicator
.frame(width: size.width, height: size.height, alignment: .center)
}
.padding(.bottom, -140)
.onAppear(perform: {
withAnimation(Animation.default.speed(0.1)){
self.progress = 1
}
})
.frame(maxHeight: .infinity, alignment: .center)
}
In order for this to work I had to set the progress to be in the 0-1 range. You can easily convert that to show 0-100. You may need to adjust the offset position of the circle of course and make any other adjustment you need. Here's the result:
Let me know if this worked for you!