tring to ad animations

This commit is contained in:
neon443
2025-05-12 17:26:37 +01:00
parent da65c3ac69
commit 025dbf22cd
7 changed files with 209 additions and 172 deletions

View File

@@ -126,6 +126,7 @@ struct EventListView: View {
) )
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
} }
.transition(.opacity)
.contextMenu() { .contextMenu() {
Button(role: .destructive) { Button(role: .destructive) {
let eventToModify = viewModel.events.firstIndex() { currEvent in let eventToModify = viewModel.events.firstIndex() { currEvent in

View File

@@ -20,7 +20,7 @@ import UserNotifications
// } // }
//} //}
struct Event: Identifiable, Codable { struct Event: Identifiable, Codable, Equatable {
var id = UUID() var id = UUID()
var name: String var name: String
var complete: Bool var complete: Bool
@@ -415,9 +415,20 @@ class EventViewModel: ObservableObject {
} }
class dummyEventViewModel: EventViewModel { class dummyEventViewModel: EventViewModel {
var template2: Event
override init(load: Bool = false) { override init(load: Bool = false) {
self.template2 = Event(
name: "template2",
complete: false,
completeDesc: "",
symbol: "hammer",
color: ColorCodable(randomColor()),
notes: "notes",
date: Date(),
recurrence: .none
)
super.init(load: false) super.init(load: false)
self.events = [self.example, self.template, self.example, self.template] self.events = [self.example, self.template, self.template2]
self.events[0].complete.toggle() self.events[0].complete.toggle()
} }
} }

View File

@@ -22,11 +22,12 @@ struct ArchiveView: View {
ForEach(viewModel.events.filter({$0.complete})) { event in ForEach(viewModel.events.filter({$0.complete})) { event in
EventListView(viewModel: viewModel, event: event) EventListView(viewModel: viewModel, event: event)
} }
.padding(.horizontal) }
.id(hey) .transition(.opacity)
.onReceive(viewModel.objectWillChange) { .padding(.horizontal)
hey = UUID() .id(hey)
} .onReceive(viewModel.objectWillChange) {
hey = UUID()
} }
} }
} }

View File

@@ -24,6 +24,8 @@ struct AddEventView: View {
@State var showNeedsNameAlert: Bool = false @State var showNeedsNameAlert: Bool = false
@State var isSymbolPickerPresented = false @State var isSymbolPickerPresented = false
@State private var bye: Bool = false
@FocusState private var focusedField: Field? @FocusState private var focusedField: Field?
private enum Field { private enum Field {
case Name, Notes case Name, Notes
@@ -32,168 +34,177 @@ struct AddEventView: View {
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
var body: some View { var body: some View {
NavigationStack { ZStack {
Form { backgroundGradient
Section( NavigationStack {
header: Form {
Text("Event Details") Section(
.font(.headline) header:
.foregroundColor(.accentColor) Text("Event Details")
) { .font(.headline)
// name & symbol .foregroundColor(.accentColor)
HStack(spacing: 5) { ) {
Button() { // name & symbol
isSymbolPickerPresented.toggle() HStack(spacing: 5) {
} label: { Button() {
Image(systemName: eventSymbol) isSymbolPickerPresented.toggle()
.resizable() } label: {
.scaledToFit() Image(systemName: eventSymbol)
.frame(width: 20, height: 20) .resizable()
.foregroundStyle(eventColor) .scaledToFit()
} .frame(width: 20, height: 20)
.frame(width: 20) .foregroundStyle(eventColor)
.buttonStyle(.borderless) }
.sheet(isPresented: $isSymbolPickerPresented) { .frame(width: 20)
SymbolsPicker( .buttonStyle(.borderless)
selection: $eventSymbol, .sheet(isPresented: $isSymbolPickerPresented) {
title: "Choose a Symbol", SymbolsPicker(
searchLabel: "Search...", selection: $eventSymbol,
autoDismiss: true) title: "Choose a Symbol",
.presentationDetents([.medium]) searchLabel: "Search...",
.apply { autoDismiss: true)
if #available(iOS 16.4, *) { .presentationDetents([.medium])
$0.presentationBackground(.ultraThinMaterial) .apply {
if #available(iOS 16.4, *) {
$0.presentationBackground(.ultraThinMaterial)
}
} }
} }
ColorPicker("", selection: $eventColor, supportsOpacity: false)
.fixedSize()
ZStack {
TextField("Event Name", text: $eventName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.trailing, eventName.isEmpty ? 0 : 30)
.animation(.spring, value: eventName)
.focused($focusedField, equals: Field.Name)
.submitLabel(.next)
.onSubmit {
focusedField = .Notes
}
MagicClearButton(text: $eventName)
}
} }
ColorPicker("", selection: $eventColor, supportsOpacity: false)
.fixedSize() // dscription
ZStack { ZStack {
TextField("Event Name", text: $eventName) TextField("Event Notes", text: $eventNotes)
.textFieldStyle(RoundedBorderTextFieldStyle()) .textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.trailing, eventName.isEmpty ? 0 : 30) .padding(.trailing, eventNotes.isEmpty ? 0 : 30)
.animation(.spring, value: eventName) .animation(.spring, value: eventNotes)
.focused($focusedField, equals: Field.Name) .focused($focusedField, equals: Field.Notes)
.submitLabel(.next) .submitLabel(.done)
.onSubmit { .onSubmit {
focusedField = .Notes focusedField = nil
} }
MagicClearButton(text: $eventName) MagicClearButton(text: $eventNotes)
} }
}
// dscription // date picker
ZStack { HStack {
TextField("Event Notes", text: $eventNotes) Spacer()
.textFieldStyle(RoundedBorderTextFieldStyle()) DatePicker("", selection: $eventDate, displayedComponents: .date)
.padding(.trailing, eventNotes.isEmpty ? 0 : 30) .datePickerStyle(WheelDatePickerStyle())
.animation(.spring, value: eventNotes) Spacer()
.focused($focusedField, equals: Field.Notes) Button() {
.submitLabel(.done) eventDate = Date()
.onSubmit { } label: {
focusedField = nil Image(systemName: "arrow.uturn.left")
.resizable()
.scaledToFit()
} }
MagicClearButton(text: $eventNotes) .buttonStyle(BorderlessButtonStyle())
} .frame(width: 20)
// date picker
HStack {
Spacer()
DatePicker("", selection: $eventDate, displayedComponents: .date)
.datePickerStyle(WheelDatePickerStyle())
Spacer()
Button() {
eventDate = Date()
} label: {
Image(systemName: "arrow.uturn.left")
.resizable()
.scaledToFit()
} }
.buttonStyle(BorderlessButtonStyle())
.frame(width: 20)
}
DatePicker( DatePicker(
"", "",
selection: $eventDate, selection: $eventDate,
displayedComponents: .hourAndMinute displayedComponents: .hourAndMinute
) )
// re-ocurrence Picker // re-ocurrence Picker
Picker("Recurrence", selection: $eventRecurrence) { Picker("Recurrence", selection: $eventRecurrence) {
ForEach(Event.RecurrenceType.allCases, id: \.self) { recurrence in ForEach(Event.RecurrenceType.allCases, id: \.self) { recurrence in
Text(recurrence.rawValue.capitalized) Text(recurrence.rawValue.capitalized)
}
} }
} .pickerStyle(SegmentedPickerStyle())
.pickerStyle(SegmentedPickerStyle()) Text(
Text( describeOccurrence(
describeOccurrence( date: eventDate,
date: eventDate, recurrence: eventRecurrence
recurrence: eventRecurrence
)
)
}
}
.scrollContentBackground(.hidden)
.navigationTitle("\(adding ? "Add Event" : "")")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
if adding {
Button() {
resetAddEventView()
dismiss()
} label: {
Image(systemName: "xmark.circle.fill")
.symbolRenderingMode(.hierarchical)
.resizable()
.scaledToFit()
.frame(width: 30)
}
}
}
ToolbarItem(placement: .topBarTrailing) {
if adding {
Button {
viewModel.addEvent(
newEvent: Event(
name: eventName,
complete: eventComplete,
completeDesc: eventCompleteDesc,
symbol: eventSymbol,
color: ColorCodable(eventColor),
notes: eventNotes,
date: eventDate,
recurrence: eventRecurrence
)
) )
resetAddEventView() )
} label: { }
Text("Save") }
.font(.headline) .scrollContentBackground(.hidden)
.cornerRadius(10) .navigationTitle("\(adding ? "Add Event" : "")")
.buttonStyle(BorderedProminentButtonStyle()) .navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
if adding {
Button() {
resetAddEventView()
dismiss()
} label: {
Image(systemName: "xmark.circle.fill")
.symbolRenderingMode(.hierarchical)
.resizable()
.scaledToFit()
.frame(width: 30)
}
} }
.disabled(eventName.isEmpty) }
.onTapGesture { ToolbarItem(placement: .topBarTrailing) {
if adding {
Button {
viewModel.addEvent(
newEvent: Event(
name: eventName,
complete: eventComplete,
completeDesc: eventCompleteDesc,
symbol: eventSymbol,
color: ColorCodable(eventColor),
notes: eventNotes,
date: eventDate,
recurrence: eventRecurrence
)
)
bye.toggle()
resetAddEventView()
} label: {
Text("Save")
.font(.headline)
.cornerRadius(10)
.buttonStyle(BorderedProminentButtonStyle())
}
.apply {
if #available(iOS 17, *) {
$0.sensoryFeedback(.success, trigger: bye)
}
}
.disabled(eventName.isEmpty)
.onTapGesture {
if eventName.isEmpty {
showNeedsNameAlert.toggle()
}
}
.alert("Missing Name", isPresented: $showNeedsNameAlert) {
Button("OK", role: .cancel) {
showNeedsNameAlert.toggle()
focusedField = .Name
}
} message: {
Text("Give your Event a name before saving.")
}
if eventName.isEmpty { if eventName.isEmpty {
showNeedsNameAlert.toggle() HStack {
} Image(systemName: "exclamationmark")
} .foregroundStyle(.red)
.alert("Missing Name", isPresented: $showNeedsNameAlert) { Text("Give your event a name.")
Button("OK", role: .cancel) { }
showNeedsNameAlert.toggle()
focusedField = .Name
}
} message: {
Text("Give your Event a name before saving.")
}
if eventName.isEmpty {
HStack {
Image(systemName: "exclamationmark")
.foregroundStyle(.red)
Text("Give your event a name.")
} }
} }
} }

View File

@@ -13,11 +13,16 @@ enum Field {
case Search case Search
} }
enum Tab { enum Tab {
case Home case home
case Archive case archive
case Statistics case stats
case Settings case settings
case Gradient }
enum FilterCategory {
case Future
case Past
case Complete
case Incomplete
} }
struct ContentView: View { struct ContentView: View {
@@ -36,7 +41,11 @@ struct ContentView: View {
@State private var searchInput: String = "" @State private var searchInput: String = ""
var filteredEvents: [Event] { var filteredEvents: [Event] {
if searchInput.isEmpty { if searchInput.isEmpty {
return viewModel.events.filter() {!$0.complete} if settingsModel.settings.showCompletedInHome {
return viewModel.events
} else {
return viewModel.events.filter() {!$0.complete}
}
} else { } else {
return viewModel.events.filter { return viewModel.events.filter {
$0.name.localizedCaseInsensitiveContains(searchInput) || $0.name.localizedCaseInsensitiveContains(searchInput) ||
@@ -44,17 +53,9 @@ struct ContentView: View {
} }
} }
} }
@State private var focusedTab: Tab = .home
var noEvents: Bool {
if viewModel.events.count == 0 {
return true
} else {
return false
}
}
@FocusState private var focusedField: Field? @FocusState private var focusedField: Field?
@FocusState private var focusedTab: Tab?
var body: some View { var body: some View {
TabView { TabView {
@@ -85,17 +86,28 @@ struct ContentView: View {
if filteredEvents.isEmpty && !searchInput.isEmpty { if filteredEvents.isEmpty && !searchInput.isEmpty {
HelpView(searchInput: $searchInput, focusedField: focusedField) HelpView(searchInput: $searchInput, focusedField: focusedField)
} else { } else {
Button("hiiiiiiiiiiiiiii") {
withAnimation() {
settingsModel.settings.showCompletedInHome.toggle()
}
}
Button("sort") {
withAnimation() {
viewModel.events.sort() { $0.date < $1.date }
}
}
ScrollView { ScrollView {
ForEach(filteredEvents) { event in ForEach(filteredEvents) { event in
EventListView(viewModel: viewModel, event: event) EventListView(viewModel: viewModel, event: event)
} }
.onDelete(perform: viewModel.removeEvent) .animation(.default, value: filteredEvents)
.transition(.opacity)
.id(hey) .id(hey)
.onReceive(viewModel.objectWillChange) { .onReceive(viewModel.objectWillChange) {
hey = UUID() hey = UUID()
} }
.padding(.horizontal) .padding(.horizontal)
if /*!searchInput.isEmpty && */filteredEvents.isEmpty { if filteredEvents.isEmpty {
HelpView( HelpView(
searchInput: $searchInput, searchInput: $searchInput,
focusedField: focusedField focusedField: focusedField
@@ -143,22 +155,22 @@ struct ContentView: View {
.tabItem { .tabItem {
Label("Home", systemImage: "house") Label("Home", systemImage: "house")
} }
.focused($focusedTab, equals: Tab.Home) .tag(Tab.home)
ArchiveView(viewModel: viewModel) ArchiveView(viewModel: viewModel)
.tabItem() { .tabItem() {
Label("Archive", systemImage: "tray.full") Label("Archive", systemImage: "tray.full")
} }
.focused($focusedTab, equals: Tab.Archive) .tag(Tab.archive)
StatsView(viewModel: viewModel) StatsView(viewModel: viewModel)
.tabItem { .tabItem {
Label("Statistics", systemImage: "chart.pie") Label("Statistics", systemImage: "chart.pie")
} }
.focused($focusedTab, equals: Tab.Statistics) .tag(Tab.stats)
SettingsView(viewModel: viewModel, settingsModel: settingsModel) SettingsView(viewModel: viewModel, settingsModel: settingsModel)
.tabItem { .tabItem {
Label("Settings", systemImage: "gear") Label("Settings", systemImage: "gear")
} }
.focused($focusedTab, equals: Tab.Settings) .tag(Tab.settings)
} }
} }
} }

View File

@@ -75,7 +75,7 @@ struct EventListView: View {
VStack { VStack {
Text("\(daysUntilEvent(event.date).long)") Text("\(daysUntilEvent(event.date).long)")
.font(.subheadline) .font(.subheadline)
.foregroundStyle(.one) .foregroundStyle(event.date.timeIntervalSinceNow < 0 ? .red : .one)
} }
Button() { Button() {
withAnimation { withAnimation {
@@ -112,6 +112,7 @@ struct EventListView: View {
.shadow(radius: 5) .shadow(radius: 5)
.padding(.trailing, 5) .padding(.trailing, 5)
} }
.transition(.opacity)
.padding(.vertical, 5) .padding(.vertical, 5)
.background(.ultraThinMaterial) .background(.ultraThinMaterial)
.overlay( .overlay(

View File

Binary file not shown.