Reputation: 11
I was trying to make the animation of a pie chart, I was trying to make the appear layout animation. (As the user navigates to the page, the pie chart will animate, starting from the 12 o'clock position and moving clockwise until it completes.) But when I try to use the new keyword sectormark
it binds the legend of the pie chart with the pie chart, so once the pie chart animation starts, the legend would follow and start rotating as well.
I am wondering if there is a way to let the legend of the pie chart remain static.
Here is the full code that I have so far with the problem:
import SwiftUI
import Charts
struct Product: Identifiable {
let id = UUID()
let title: String
let revenue: Double
}
struct FirstView: View {
@State private var animateChart: Bool = false
@State private var products: [Product] = [
.init(title: "Annual", revenue: 0.1),
.init(title: "Monthly", revenue: 0.2),
.init(title: "Lifetime", revenue: 0.7)
]
@State private var selectedProduct: Product?
@State private var isDetailLocked: Bool = false
@State private var touchLocation: CGPoint?
var body: some View {
VStack {
// Refresh button
Button(action: {
// Reset the animation
withAnimation(.easeInOut(duration: 0.5)) {
animateChart = false
}
// Trigger the animation after a slight delay for reset effect
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
withAnimation(.easeInOut(duration: 1.5)) {
animateChart = true
}
}
}) {
Text("Refresh Chart")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.padding()
ZStack {
// Doughnut Chart section with smaller size
Chart(products) { product in
SectorMark(
angle: .value(
Text(verbatim: product.title),
animateChart ? product.revenue : 0 // Start from 0
),
innerRadius: .ratio(0.6),
angularInset: 2
)
.cornerRadius(6)
.foregroundStyle(
by: .value(
Text(verbatim: product.title),
product.title
)
)
}
.rotationEffect(.degrees(animateChart ? 0 : -180))
.frame(width: 200, height: 200) // Smaller chart size
// Adding an overlay to handle taps
GeometryReader { geometry in
Color.clear
.contentShape(Rectangle())
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
guard !isDetailLocked else { return }
let touchLocation = value.location
self.touchLocation = touchLocation
let center = CGPoint(
x: geometry.size.width / 2,
y: geometry.size.height / 2
)
let touchAngle = angleFromPoint(center: center, point: touchLocation)
let totalRevenue = products.map(\.revenue).reduce(0, +)
var currentAngle: Double = 0
for product in products {
let productAngle = 360 * (product.revenue / totalRevenue)
if touchAngle >= currentAngle && touchAngle <= currentAngle + productAngle {
selectedProduct = product
break
}
currentAngle += productAngle
}
}
.onEnded { _ in
if !isDetailLocked {
selectedProduct = nil
}
}
)
.simultaneousGesture(
TapGesture(count: 2)
.onEnded {
isDetailLocked.toggle()
}
)
}
// Tooltip view for showing details
if let selectedProduct = selectedProduct, let touchLocation = touchLocation {
VStack {
Text("\(selectedProduct.title)")
.font(.headline)
.padding(4)
Text("Revenue: \(selectedProduct.revenue * 100, specifier: "%.1f")%")
.font(.subheadline)
.padding(4)
}
.background(Color.white)
.cornerRadius(8)
.shadow(radius: 5)
.position(x: touchLocation.x, y: touchLocation.y - 50)
.animation(.easeInOut)
}
}
.onAppear {
// Trigger the animation after the view appears
withAnimation(.easeInOut(duration: 1.5)) {
animateChart = true
}
}
}
.background(Color.white)
}
// Function to calculate angle from center to a given point
private func angleFromPoint(center: CGPoint, point: CGPoint) -> Double {
let deltaX = point.x - center.x
let deltaY = point.y - center.y
let radians = atan2(deltaY, deltaX)
var degrees = radians * 180 / .pi
if degrees < 0 {
degrees += 360
}
// Adjust for the 90-degree rotation of the chart
degrees += 90
if degrees >= 360 {
degrees -= 360
}
return degrees
}
}
I am expecting to fix this bug, let the legend of the pie chart remain static when animation active.
Upvotes: 1
Views: 108