I am making quiz app where in need to create timer that look like this image
So it doesn't trim in normal way but I has to work like hands of the clock. I've tried to use custom shapes to actually draw it by myself because I know the center point, radius and starting point. So I spent few hours on that and I give up, maybe you can tell me if there is an easier way or you know why my code isn't working as supposed to.
Here is my custom Shape:
struct ClockShape: Shape {
var trim: Double
func properPoint(_ rect: CGRect) -> CGPoint {
let r: CGFloat = 2 //rect.height/2
let s: CGFloat = 2 //rect.midX
let tempLength: CGFloat = sqrt(abs(2*pow(r, 2) - (4 * r * cos(360 * trim))))
let length = sqrt(abs(tempLength))
let a: CGFloat = ((pow(r, 2) - sqrt(abs(length))) / (-2 * s)) - s
let b: CGFloat = sqrt(abs(sqrt(abs(length)) - pow((a - s), 2)))
return CGPoint(x: a, y: b)
}
func path(in rect: CGRect) -> Path {
var path = Path()
let point = properPoint(rect)
path.move(to: CGPoint(x: rect.midX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.midX, y: rect.midY))
path.addLine(to: CGPoint(x: point.x, y: point.y))
path.addLine(to: CGPoint(x: rect.midX, y: rect.minY))
return path
}
}
Here is a ContentView:
struct ContentView: View {
var body: some View {
VStack {
ClockShape(trim: 0.5)
.fill(Color.accentColor)
.frame(width: 100, height: 100)
}
}
}
It should draw a circle trimmed by a value of 'trim' (e.g 0.5 -> 50%)
CodePudding user response:
You're overcomplicating the problem. To draw a trimmed circle, use Circle()
and .trim
. Here's an example that scales to fit its frame, with animation.
struct ContentView: View {
@State private var value: Double = 0
var body: some View {
VStack {
clock(value)
.frame(width: 120, height: 120)
Slider(value: $value, in: 0...10)
}
.padding()
}
func clock(_ value: Double) -> some View {
GeometryReader { proxy in
ZStack {
Circle()
.fill(.pink.opacity(0.3))
Circle()
.inset(by: proxy.size.width / 4)
.trim(from: 0, to: value / 10)
.stroke(.pink.opacity(0.6), style: StrokeStyle(lineWidth: proxy.size.width / 2))
.rotationEffect(.radians(-.pi/2))
.animation(.linear, value: value)
Text("\(Int(value))")
.font(Font.system(size: proxy.size.width / 2))
.bold()
.foregroundColor(.white)
}
}
}
}