Confused as to why the Chart flips with each user input. The console is also output unique id for each slice which was not my intention. Not sure if the unique .id is the culprit behind the flip.
selectedCount changed to: Optional(3)
Selected slice: Optional(App.EmojiUsage(id: 69090646-0D0A-4FE8-86EC-4103608DC3F7, emojiTab: App.emojiTab.sad, count: 1))
Scheduling reset task to run in 2 seconds
Resetting selected slice and count
selectedCount changed to: Optional(1)
Selected slice: Optional(App.EmojiUsage(id: DE4A76D1-CC57-4FA0-A261-9AD1A6E28F95, emojiTab: App.emojiTab.happy, count: 2))
Scheduling reset task to run in 2 seconds
Resetting selected slice and count
selectedCount changed to: Optional(3)
Selected slice: Optional(App.EmojiUsage(id: 5052F8EA-2582-4E72-A61D-01FCCDF3DB03, emojiTab: App.emojiTab.sad, count: 1))
Scheduling reset task to run in 2 seconds
Resetting selected slice and count
selectedCount changed to: Optional(0)
Selected slice: Optional(App.EmojiUsage(id: 5C1AB577-6CFC-4BA8-A9DF-30822EF79B91, emojiTab: App.emojiTab.happy, count: 2))
Scheduling reset task to run in 2 seconds
@Model
class AppModel {
var id: String
var journalEntry: String
var date: Date
var emojiTab: emojiTab
init(journalEntry: String, date: Date, emojiTab: emojiTab) {
self.id = UUID().uuidString
self.journalEntry = journalEntry
self.date = date
self.emojiTab = emojiTab
}
}
struct EmojiPrompt: Identifiable {
var id = UUID()
var icon: RiveViewModel
var emojitab: emojiTab
var title: String
}
enum emojiTab: String, Codable, Plottable {
case happy
case sad
case sleep
var primitivePlottable: Double {
switch self {
case .sleep:
return 0.0
case .happy:
return 1.0
case .sad:
return 2.0
}
}
}
var emojiPrompt = [
EmojiPrompt(
icon: RiveViewModel(
fileName: "app",
stateMachineName: "happyBtnSM",
artboardName: "happyBtn"
),
emojitab: .happy,
title: "Happy 1"
),
EmojiPrompt(
icon: RiveViewModel(
fileName: "app",
stateMachineName: "sadBtnSM",
artboardName: "sadBtn"
),
emojitab: .sad,
title: "Sad 2"
),
EmojiPrompt(
icon: RiveViewModel(
fileName: "app",
stateMachineName: "happyBtnSM",
artboardName: "happyBtn"
),
emojitab: .sleep,
title: "Sleep"
)
]
import SwiftUI
import SwiftData
import RiveRuntime
import Charts
struct SectorChartView: View {
@Environment(\.modelContext) private var context: ModelContext
@Binding var selectedEmojiUsage: EmojiUsage?
@State private var selectedCount: Int?
@Binding var selectedSlice: EmojiUsage?
@State private var resetTask: DispatchWorkItem? // State variable for the reset task
var emojiUsageData: [EmojiUsage]
var resetDelay: TimeInterval = 2.0 // Adjustable delay for reset
var body: some View {
ZStack {
Chart {
ForEach(emojiUsageData) { data in
SectorMark(
angle: .value("Count", data.count),
innerRadius: .ratio(0.70),
outerRadius: selectedSlice?.emojiTab == data.emojiTab ? .ratio(1.0) : .ratio(0.75),
angularInset: 1.5
)
.cornerRadius(4)
.foregroundStyle(by: .value("Emoji", data.emojiTab.rawValue.capitalized))
}
}
.chartAngleSelection(value: $selectedCount)
.chartBackground { chartProxy in
GeometryReader { geo in
let frame = geo[chartProxy.plotFrame!]
VStack {
if let selectedEmojiUsage = selectedEmojiUsage {
RiveViewModel(fileName: "app", stateMachineName: "\(selectedEmojiUsage.emojiTab.rawValue)BtnSM", artboardName: "\(selectedEmojiUsage.emojiTab.rawValue)Btn")
.view()
.frame(width: 120, height: 120)
.id(selectedEmojiUsage.emojiTab.rawValue) // Force re-render when the emojiTab changes
} else {
RiveViewModel(fileName: "app", stateMachineName: "sleepBtnSM", artboardName: "sleepBtn")
.view()
.frame(width: 120, height: 120)
.id("sleep") // Force re-render when default state
}
}
.position(x: frame.midX, y: frame.midY)
}
}
}
.onChange(of: selectedCount) { oldValue, newValue in
// Ensure reset task is only scheduled if there is a valid new value
guard newValue != nil else { return }
resetTask?.cancel() // Cancel any existing reset task
print("selectedCount changed to: \(String(describing: newValue))")
if let newValue = newValue {
withAnimation {
getSelectedSlice(value: newValue)
}
let task = DispatchWorkItem {
withAnimation(.easeIn) {
print("Resetting selected slice and count")
self.selectedSlice = nil
self.selectedCount = nil
self.selectedEmojiUsage = nil
}
}
resetTask = task
print("Scheduling reset task to run in 2 seconds")
DispatchQueue.main.asyncAfter(deadline: .now() + resetDelay, execute: task) // Schedule reset after specified delay
}
}
.frame(width: 250, height: 250)
}
private func getSelectedSlice(value: Int) {
var cumulativeTotal = 0
_ = emojiUsageData.first { emojiRange in
cumulativeTotal += emojiRange.count
if value <= cumulativeTotal {
selectedSlice = emojiRange
selectedEmojiUsage = emojiRange
print("Selected slice: \(String(describing: selectedSlice))")
return true
}
return false
}
}
}
var emojiUsageData: [EmojiUsage] {
let groupedEntries = Dictionary(grouping: entries, by: { $0.emojiTab })
return groupedEntries.map { (key, value) in
EmojiUsage(emojiTab: key, count: value.count)
}
}
struct EmojiUsage: Identifiable {
var id = UUID()
var emojiTab: emojiTab
var count: Int
}
Post
Replies
Boosts
Views
Activity
import SwiftUI
import RiveRuntime
struct MenuCardView: View {
@State private var CardOne = false
@State private var CardTwo: Bool = false
@AppStorage("selectedCard") var selectedCard: SelectedCard = .CardOne
var body: some View {
ZStack {
ScrollView (.horizontal, showsIndicators: true ) {
VStack(spacing:0) {
TabView {
CardView
}
.tabViewStyle(.page)
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
}
}
if CardOne {
Moon_Event()
.frame(width: 400, height: 780)
.opacity(CardOne ? 1 : 0)
.offset(y: CardOne ? 20 : 0)
.overlay (
Button {
withAnimation(.spring()) {
CardOne.toggle()
}
} label: {
Image(systemName: "xmark")
.frame(width: 40, height: 40)
.foregroundStyle(.black)
.background(.white)
.mask(Circle())
.shadow(color: Color("Shadow").opacity(0.3) ,radius: 5, x: 0, y:3)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
.offset(y: CardOne ? 15 : 200)
)
.transition(.opacity.combined(with: .move(edge: .top)))
.zIndex(1)
}
if CardTwo {
Sun_Event()
.frame(width: 400, height: 780)
.opacity(CardTwo ? 1 : 0)
.offset(y: CardTwo ? 20 : 0)
.overlay (
Button {
withAnimation(.spring()) {
CardTwo.toggle()
}
} label: {
Image(systemName: "xmark")
.frame(width: 40, height: 40)
.foregroundStyle(.black)
.background(.white)
.mask(Circle())
.shadow(color: Color("Shadow").opacity(0.3) ,radius: 5, x: 0, y:3)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
.offset(y: CardTwo ? 15 : 200)
)
.transition(.opacity.combined(with: .move(edge: .top)))
.zIndex(1)
}
}
}
}
var CardView: some View {
ForEach(menuItems) { items in
GeometryReader { proxy in
items.rvm.view()
.padding(.vertical, 220)
.rotation3DEffect(.degrees(proxy.frame(in: .global).minX / -10),
axis: (x: 0, y: 1, z: 0))
.shadow(color: Color("Shadow").opacity(0.8), radius: 10, x: 0, y: 10)
.onTapGesture { location in
withAnimation(.spring()) {
print("Location: \(location)")
// items.message.toggle() nor selectedCard.toggle() seem to work. I'm just trying to create a simple .toggle() for which items is on the screen.//
}
}
Text("\(items.message)")
.padding(.top, 290)
}
}
.padding(20)
.frame(height: UIScreen.main.bounds.height)
}
struct MenuItems: Identifiable {
var id = UUID()
var rvm: RiveViewModel
var modal: SelectedCard
var message: String
}
var menuItems = [
MenuItems(rvm: RiveViewModel(fileName: "mooncard"), modal: .CardOne, message: "CardOne"),
MenuItems(rvm: RiveViewModel(fileName: "suncard"), modal: .CardTwo, message: "CardTwo"),
MenuItems(rvm: RiveViewModel(fileName: "mooncard"), modal: .CardThree, message: "CardThree"),
MenuItems(rvm: RiveViewModel(fileName: "suncard"), modal: .CardFour, message: "CardFour")
]
enum SelectedCard: String {
case CardOne
case CardTwo
case CardThree
case CardFour
}
#Preview {
MenuCardView()
}