Drawing a Pie without Path?

Drawing a pie isn't difficult if I do it with Path.

import SwiftUI

struct ContentView8: View {
    var body: some View {
		PieSlice(start: .degrees(-90), end: .degrees(120))
			.fill(.pink)
    }
}

struct PieSlice: Shape {
	let start: Angle
	let end: Angle
	func path(in rect: CGRect) -> Path {
		var path = Path()
		let center = CGPoint(x: rect.midX, y: rect.midY)
		path.move(to: center)
		path.addArc(center: center, radius: rect.midX, startAngle: start, endAngle: end, clockwise: false)
		return path
	}
}

Actually, I want to animate this pie such that it will gradually deploy starting at -90 degrees. In the code above, I suppose I cannot animate the pie because the PieSlice guy isn't a View. Or can I? If I can't, is there an alternative way of drawing a pie so that I can animate it?

Thanks a million.

Señor Tomato Source

Hostage Negotiator at

Tomato Source Association of North America

Answered by BabyJ in 758303022

You can override the default Animatable implementation for Shape and provide your own for your custom pie slice. One small problem, however, is that Animatable requires the value(s) to be animated conform to VectorArithmetic. You are using the Angle type which does not conform. You can either add the conformance yourself, or use a type that does conform, such as Double.

Here is an example that animates changes to the pie slice correctly:

struct ContentView: View {
    @State private var amount = 0.0

    var body: some View {
        PieSlice(start: -90, end: amount)
            .fill(.pink)
            .onTapGesture {
                withAnimation {
                    amount += 30
                }
            }
    }
}

struct PieSlice: Shape {
    var start: Double
    var end: Double

    var animatableData: AnimatablePair<Double, Double> {
        get {
            .init(start, end)
        }
        set {
            start = newValue.first
            end = newValue.second
        }
    }

    func path(in rect: CGRect) -> Path {
        var path = Path()
        let center = CGPoint(x: rect.midX, y: rect.midY)
        path.move(to: center)
        path.addArc(center: center, radius: rect.midX, startAngle: .degrees(start), endAngle: .degrees(end), clockwise: false)
        return path
    }
}

It's cheesy, but I guess one way of doing it is to use Circle().stroke.

Accepted Answer

You can override the default Animatable implementation for Shape and provide your own for your custom pie slice. One small problem, however, is that Animatable requires the value(s) to be animated conform to VectorArithmetic. You are using the Angle type which does not conform. You can either add the conformance yourself, or use a type that does conform, such as Double.

Here is an example that animates changes to the pie slice correctly:

struct ContentView: View {
    @State private var amount = 0.0

    var body: some View {
        PieSlice(start: -90, end: amount)
            .fill(.pink)
            .onTapGesture {
                withAnimation {
                    amount += 30
                }
            }
    }
}

struct PieSlice: Shape {
    var start: Double
    var end: Double

    var animatableData: AnimatablePair<Double, Double> {
        get {
            .init(start, end)
        }
        set {
            start = newValue.first
            end = newValue.second
        }
    }

    func path(in rect: CGRect) -> Path {
        var path = Path()
        let center = CGPoint(x: rect.midX, y: rect.midY)
        path.move(to: center)
        path.addArc(center: center, radius: rect.midX, startAngle: .degrees(start), endAngle: .degrees(end), clockwise: false)
        return path
    }
}
Drawing a Pie without Path?
 
 
Q