Skip to content

Instantly share code, notes, and snippets.

@Max094Reikeb
Created April 13, 2024 19:40
Show Gist options
  • Save Max094Reikeb/a99e1f22356a4ce44d45bda1b82ba7d6 to your computer and use it in GitHub Desktop.
Save Max094Reikeb/a99e1f22356a4ce44d45bda1b82ba7d6 to your computer and use it in GitHub Desktop.
My chart
import Charts
import SwiftData
import SwiftUI
struct MainCategoryChart: View {
@AppStorage("preferedCurrencyCode") private var preferedCurrencyCode = Currency.preview.first!.code
@Query private var subscriptions: [Subscription]
@Query private var categories: [Category]
@State var selectedPrices: Double? = nil
@Binding var timeFrame: TimeFrame
var updatedCategories: [Category] {
return categories.sorted(by: { $0.name.lowercased() < $1.name.lowercased() })
}
private var cumulativePricesRangesForCategories: [(category: Category, range: Range<Double>)] {
var cumulative = 0.0
return updatedCategories.map { category in
let totalCost = category.getSubscriptionsCost(timeFrame: timeFrame, preferredCurrency: Currency.preview.first(where: { $0.code == preferedCurrencyCode })!)
let newCumulative = cumulative + totalCost
let result = (category: category, range: cumulative..<newCumulative)
cumulative = newCumulative
return result
}
}
var selectedCategory: Category? {
if let selectedPrices,
let selectedIndex = cumulativePricesRangesForCategories
.firstIndex(where: { $0.range.contains(selectedPrices) }) {
return updatedCategories[selectedIndex]
}
return nil
}
var body: some View {
Chart(updatedCategories, id: \.name) { element in
SectorMark(
angle: .value("Price", element.getSubscriptionsCost(timeFrame: timeFrame, preferredCurrency: Currency.preview.first(where: { $0.code == preferedCurrencyCode })!)),
innerRadius: .ratio(0.618),
angularInset: 1.5
)
.cornerRadius(5.0)
.foregroundStyle(by: .value("Name", element.name))
.opacity(selectedCategory == nil ? 1 : element.name == (selectedCategory?.name ?? "") ? 1 : 0.3)
}
.chartLegend(alignment: .center, spacing: 18)
.chartAngleSelection(value: $selectedPrices)
.scaledToFit()
#if os(macOS)
.transaction {
$0.animation = nil // Do not animate on macOS
}
#endif
.chartBackground { chartProxy in
GeometryReader { geometry in
let frame = geometry[chartProxy.plotFrame!]
VStack {
Text(timeFrame == .days ? "Monthly" : "Yearly")
.font(.callout)
.foregroundStyle(.secondary)
.opacity(selectedCategory == nil ? 1 : 0)
if let selectedCat = selectedCategory {
Text(selectedCat.getSubscriptionsCost(timeFrame: timeFrame, preferredCurrency: Currency.preview.first(where: { $0.code == preferedCurrencyCode })!), format: .currency(code: Currency.preview.first(where: { $0.code == preferedCurrencyCode })!.code).precision(.fractionLength(2)).presentation(.narrow))
.font(.title2.bold())
.foregroundStyle(Color.primary)
} else {
Text(totalCost(), format: .currency(code: Currency.preview.first(where: { $0.code == preferedCurrencyCode })!.code).precision(.fractionLength(2)).presentation(.narrow))
.font(.title2.bold())
.foregroundStyle(Color.primary)
}
Text(selectedCategory == nil ? String(localized: "Average", comment: "Average monthly/yearly total subscription cost") : selectedCategory?.name ?? "???")
.font(.callout)
.foregroundStyle(Color.secondary)
}
.position(x: frame.midX, y: frame.midY)
}
}
}
func totalCost() -> Double {
if timeFrame == .days {
return subscriptions.reduce(0) { $0 + $1.costPerMonth(preferredCurrency: Currency.preview.first(where: { $0.code == preferedCurrencyCode })!) }
} else {
return subscriptions.reduce(0) { $0 + $1.costPerYear(preferredCurrency: Currency.preview.first(where: { $0.code == preferedCurrencyCode })!) }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment