This nested ring UI works well but how can I code it so it scales whether its parent is very small or very large?
import SwiftUI
struct CustomGaugeStyleView: View {
@State private var innerRingFill = 6.5
var body: some View {
Gauge(value: innerRingFill, in: 0...10) {
Image(systemName: "gauge.medium")
.font(.system(size: 50.0))
} currentValueLabel: {
Text("(innerRingFill.formatted(.number))")
}
.gaugeStyle(twoRingGaugeStyle(outerRingMin: 5.5, outerRingMax: 7.5))
}
}
struct CustomGaugeStyleView_Previews: PreviewProvider {
static var previews: some View {
CustomGaugeStyleView()
}
}
struct twoRingGaugeStyle: GaugeStyle {
var outerRingMin: Double
var outerRingMax: Double
func makeBody(configuration: Configuration) -> some View {
GeometryReader { geometry in
ZStack {
Circle()
.stroke(Color(.lightGray).opacity(0.2), style: StrokeStyle(lineWidth: 20))
.frame(height: geometry.size.height * 0.70)
Circle()
.trim(from: 0, to: 0.75 * configuration.value)
.stroke(Color.orange.gradient, style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .round))
.rotationEffect(.degrees(270))
.frame(height: geometry.size.height * 0.70)
Circle()
.trim(from: outerRingMin / 10, to: outerRingMax / 10)
.stroke(Color.green.gradient, style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .round))
.rotationEffect(.degrees(270))
.frame(height: geometry.size.height * 0.82)
}
.padding()
}
.aspectRatio(contentMode: .fit)
}
}
the first image is the view without any frame size, the second view is with adding .frame(height: 100)
to the Gauge.
2
Answers
I’ve run your code myself, and I’ve checked that the raw value of the green Circle() is incorrect.
And, as @lorem ipsum said, to solve overlapping problems, you need to modify the code so that
lineWidth:
works as percentage.As @loremipsum mentioned in the comments, if you want this UI to scale for any screen size, then your
lineWidth
needs to be a percentage.Here is an example implementation:
Explanation
There are only a few changes to the code here, and all of them are inside the
twoRingGuageStyle
.lineWidth
inside theStrokeStyle
, I changed the value from20
togeometry.size.width * multiplierAmount
. This makes the line width also scale with theGeometryReader
.multiplierAmount
, to the top of thetwoRingGuageStyle
. This allows you to optionally configure theCircle
width. The default value is0.045
.frame
on the outerCircle
atheight: geometry.size.height * 0.82
, as I mentioned in the comment on that line,I might add a
minHeight
above, or the circle will end up eventually having not enough of a change between the inner value to appear separated.Otherwise, at very small screen sizes, your circles will appear to not be spaced out enough.
Screenshots
Note
This code was tested with Xcode 14.2 and macOS 13.1. It may require minute adjustments of the values for the UI to be of the exact look needed.