I'm looking to add text inside the arc's, but I'm not finding that I can overlay text. For testing I'm just trying to get the angle text in earthyStatsInTonnes. I've tried using GeometryReader but the font just ends up overlapping each other in the center.
struct Shapes: View {
private enum Params {
static var viewSizeOffset: CGFloat = 100
static let lineWidth: CGFloat = 20
static var halfLineWidth: CGFloat {
lineWidth / 2
}
static let animationDuration: CGFloat =
0.05 // nominal animation to smooth things
static let circleDegrees: CGFloat = 360
static let circleRotation: Angle = .init(degrees: -90)
static let offsetFactor: CGFloat = 0.4
static let minCircleAngle: CGFloat = 00
static let maxCircleAngle: CGFloat = 360
static var minCircleTrim: CGFloat {
minCircleAngle / circleDegrees
}
static var maxCircleTrim: CGFloat {
maxCircleAngle / circleDegrees
}
static let progressColor = Color.red
static let backgroundColor = Color.blue
static let arcFrame: CGFloat = 225
}
@State private var rotation: Double = 0
@State var earthyStatsInTonnes = [2.6, 3.0, 5.4, 6.75, 0.0]
@State private var increment = 0
let arcColor = [Color.red, Color.gray, Color.black, Color.green, Color.purple, Color.blue]
var body: some View {
GeometryReader { geo in
VStack {
ZStack {
Text("Text \(angle)")
.position(
x: 0,
y: 0
)
Circle()
.fill(.red.opacity(0.2))
.frame(width: Params.arcFrame + 36, height: Params.arcFrame + 36)
ForEach(earthyStatsInTonnes.indices) { stat in
VStack {
ZStack {
arcView(startAngle: stat == 0 ? 00 : earthyStatsInTonnes[stat],
endAngle: earthyStatsInTonnes[stat],
total: 17.8,
arcColor: arcColor[stat])
}
.zIndex(-Double(stat))
}
}
}
.frame(width: Params.arcFrame, height: Params.arcFrame)
.padding(45)
}
.position(x: geo.size.width / 2, y: geo.size.height / 2)
}
}
private func arcView(startAngle: Double, endAngle: Double, total: Double, arcColor: Color) -> some View {
return Arc(startAngle: .degrees(startAngle * total),
endAngle: .degrees(startAngle * total + (endAngle * total)),
clockwise: true)
.stroke(arcColor,
style: .init(lineWidth: 36,
lineCap: .round,
lineJoin: .miter,
miterLimit: 0,
dash: [2, 3],
dashPhase: 1))
.frame(width: 226, height: 229)
}
func onDrag(value: DragGesture.Value) {
let circleDegrees = Params.circleDegrees
// Calculate radians
let vector = CGVector(dx: value.location.x, dy: value.location.y)
let radians = atan2(
vector.dy - Params.halfLineWidth,
vector.dx - Params.halfLineWidth
)
var angle = radians * circleDegrees / 2 / .pi
// atan2 gives -180 to 180, so set range to 0 to 360
if angle < 0 {
angle += circleDegrees
}
// constrain angle between desired range
angle = max(angle, Params.minCircleAngle)
angle = min(angle, Params.maxCircleAngle)
withAnimation(Animation.linear(duration: Params.animationDuration)) {
self.angle = Double(angle)
}
}
}
struct Arc: Shape {
var startAngle: Angle
var endAngle: Angle
var clockwise: Bool
func path(in rect: CGRect) -> Path {
let rotationAdjustment = Angle.degrees(90)
let modifiedStart = startAngle - rotationAdjustment
let modifiedEnd = endAngle - rotationAdjustment
var path = Path()
path.addArc(center: CGPoint(x: rect.midX, y: rect.midY),
radius: rect.width / 2,
startAngle: modifiedStart,
endAngle: modifiedEnd,
clockwise: !clockwise)
return path
}
}
