🚧 First draw of colored days in calendar
This commit is contained in:
@@ -57,6 +57,41 @@ final class Subscription: Identifiable {
|
|||||||
}
|
}
|
||||||
return remaining
|
return remaining
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPaymentDaysIn(year: Int, month: Int) -> [Int] {
|
||||||
|
var paymentDays: [Int] = []
|
||||||
|
|
||||||
|
let calendar = Calendar.current
|
||||||
|
let components = DateComponents(year: year, month: month)
|
||||||
|
guard let date = calendar.date(from: components) else {
|
||||||
|
return paymentDays
|
||||||
|
}
|
||||||
|
|
||||||
|
for payment in payments {
|
||||||
|
var currentDate = payment.startDate
|
||||||
|
while calendar.isDate(currentDate, equalTo: date, toGranularity: .month) {
|
||||||
|
let day = calendar.component(.day, from: currentDate)
|
||||||
|
paymentDays.append(day)
|
||||||
|
|
||||||
|
switch payment.intervall {
|
||||||
|
case .weekly:
|
||||||
|
guard let nextDate = calendar.date(byAdding: .day, value: 7, to: currentDate) else { break }
|
||||||
|
currentDate = nextDate
|
||||||
|
case .monthly:
|
||||||
|
guard let nextDate = calendar.date(byAdding: .month, value: 1, to: currentDate) else { break }
|
||||||
|
currentDate = nextDate
|
||||||
|
case .quarter:
|
||||||
|
guard let nextDate = calendar.date(byAdding: .month, value: 3, to: currentDate) else { break }
|
||||||
|
currentDate = nextDate
|
||||||
|
case .yearly:
|
||||||
|
guard let nextDate = calendar.date(byAdding: .year, value: 1, to: currentDate) else { break }
|
||||||
|
currentDate = nextDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paymentDays
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Payment: Identifiable {
|
final class Payment: Identifiable {
|
||||||
|
|||||||
@@ -1,20 +1,39 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
|
@State private var subs: [Subscription] = []
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self._subs = State(initialValue: [
|
||||||
|
Subscription(name: "Test", payments: [Payment(amount: 9.99, intervall: .monthly, startDate: getDate(from: "2023-01-01"))], color: .blue),
|
||||||
|
Subscription(name: "Fitness First", payments: [
|
||||||
|
Payment(amount: 7.9, intervall: .weekly, startDate: getDate(from: "2023-01-23")),
|
||||||
|
Payment(amount: 29, intervall: .quarter, startDate: getDate(from: "2023-04-03"))
|
||||||
|
], color: .red)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TabView {
|
TabView {
|
||||||
HomeView()
|
HomeView(subs: $subs)
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Image(systemName: "house.fill")
|
Image(systemName: "house.fill")
|
||||||
Text("Home")
|
Text("Home")
|
||||||
}
|
}
|
||||||
|
|
||||||
PaymentCalendarView()
|
PaymentCalendarView(subs: $subs)
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Image(systemName: "calendar")
|
Image(systemName: "calendar")
|
||||||
Text("Calendar")
|
Text("Calendar")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.background(Color.gray.opacity(0.2))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getDate(from dateString: String) -> Date {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "yyyy-MM-dd"
|
||||||
|
return formatter.date(from: dateString) ?? Date()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,8 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct HomeView: View {
|
struct HomeView: View {
|
||||||
|
@Binding var subs: [Subscription]
|
||||||
@State private var showAddSubscriptionSheet = false
|
@State private var showAddSubscriptionSheet = false
|
||||||
@State private var subs: [Subscription] = []
|
|
||||||
|
|
||||||
init() {
|
|
||||||
self._subs = State(initialValue: [
|
|
||||||
Subscription(name: "Test", payments: [Payment(amount: 9.99, intervall: .monthly, startDate: self.getDate(from: "2023-01-01"))], color: .blue),
|
|
||||||
Subscription(name: "Fitness First", payments: [
|
|
||||||
Payment(amount: 7.9, intervall: .weekly, startDate: self.getDate(from: "2023-01-23")),
|
|
||||||
Payment(amount: 29, intervall: .quarter, startDate: self.getDate(from: "2023-04-03"))
|
|
||||||
], color: .red)
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
@@ -105,22 +95,16 @@ struct HomeView: View {
|
|||||||
}
|
}
|
||||||
return remainingTotal
|
return remainingTotal
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDate(from dateString: String) -> Date {
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.dateFormat = "yyyy-MM-dd"
|
|
||||||
return formatter.date(from: dateString) ?? Date()
|
|
||||||
}
|
|
||||||
|
|
||||||
func formattedDate(_ date: Date) -> String {
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.dateStyle = .medium
|
|
||||||
return formatter.string(from: date)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HomeView_Previews: PreviewProvider {
|
struct HomeView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
HomeView()
|
HomeView(subs: .constant([
|
||||||
|
Subscription(name: "Test", payments: [Payment(amount: 9.99, intervall: .monthly, startDate: Date())], color: .blue),
|
||||||
|
Subscription(name: "Fitness First", payments: [
|
||||||
|
Payment(amount: 7.9, intervall: .weekly, startDate: Date()),
|
||||||
|
Payment(amount: 29, intervall: .quarter, startDate: Date())
|
||||||
|
], color: .red)
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,131 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct PaymentCalendarView: View {
|
struct PaymentCalendarView: View {
|
||||||
|
@Binding var subs: [Subscription]
|
||||||
|
let calendar = Calendar.current
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
|
||||||
|
@State private var currentDate = Date()
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
Text("Payment Calendar")
|
Text("Payment Calendar")
|
||||||
.font(.title)
|
.font(.title)
|
||||||
.padding()
|
.padding()
|
||||||
Spacer()
|
|
||||||
|
Text(currentMonthYear())
|
||||||
|
.font(.headline)
|
||||||
|
.padding()
|
||||||
|
|
||||||
|
calendarView()
|
||||||
}
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentMonthYear() -> String {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "MMMM yyyy"
|
||||||
|
return formatter.string(from: currentDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func calendarView() -> some View {
|
||||||
|
let days = daysInMonth(date: currentDate)
|
||||||
|
let firstDay = firstDayOfMonth(date: currentDate)
|
||||||
|
|
||||||
|
let columns = Array(repeating: GridItem(.flexible()), count: 7)
|
||||||
|
let weeks = (days.count + firstDay) / 7 + ((days.count + firstDay) % 7 == 0 ? 0 : 1)
|
||||||
|
|
||||||
|
return LazyVGrid(columns: columns, spacing: 20) {
|
||||||
|
ForEach(0..<7, id: \.self) { index in
|
||||||
|
Text(daySymbol(index: index))
|
||||||
|
.fontWeight(.bold)
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEach(0..<(weeks * 7), id: \.self) { index in
|
||||||
|
if index < firstDay || index - firstDay >= days.count {
|
||||||
|
Text("")
|
||||||
|
} else {
|
||||||
|
let day = index - firstDay + 1
|
||||||
|
let dayDate = calendar.date(byAdding: .day, value: day - 1, to: startOfMonth(date: currentDate))!
|
||||||
|
let paymentColor = getPaymentColor(for: dayDate)
|
||||||
|
|
||||||
|
Text("\(day)")
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.background(paymentColor)
|
||||||
|
.cornerRadius(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPaymentColor(for date: Date) -> Color {
|
||||||
|
for sub in subs {
|
||||||
|
for payment in sub.payments {
|
||||||
|
if isPaymentDue(payment: payment, for: date) {
|
||||||
|
return sub.color.opacity(0.3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Color.clear
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPaymentDue(payment: Payment, for date: Date) -> Bool {
|
||||||
|
switch payment.intervall {
|
||||||
|
case .weekly:
|
||||||
|
let startOfWeek = calendar.date(from: calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: date))!
|
||||||
|
let endOfWeek = calendar.date(byAdding: .day, value: 6, to: startOfWeek)!
|
||||||
|
return date >= startOfWeek && date <= endOfWeek && calendar.component(.weekday, from: payment.startDate) == calendar.component(.weekday, from: date)
|
||||||
|
|
||||||
|
case .monthly:
|
||||||
|
// ToDo: Payment not visible if Payment is at example at 31th of month, but month only has 30 days
|
||||||
|
return calendar.component(.day, from: payment.startDate) == calendar.component(.day, from: date)
|
||||||
|
|
||||||
|
case .quarter:
|
||||||
|
guard let quarterStartDate = calendar.date(byAdding: .month, value: -3, to: payment.startDate) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let startOfQuarter = calendar.date(from: calendar.dateComponents([.year, .month], from: quarterStartDate))!
|
||||||
|
let endOfQuarter = calendar.date(byAdding: .month, value: 3, to: startOfQuarter)!
|
||||||
|
return date >= startOfQuarter && date <= endOfQuarter
|
||||||
|
|
||||||
|
case .yearly:
|
||||||
|
return calendar.component(.year, from: payment.startDate) == calendar.component(.year, from: date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func daysInMonth(date: Date) -> [Date] {
|
||||||
|
guard let range = calendar.range(of: .day, in: .month, for: date) else { return [] }
|
||||||
|
return range.compactMap { day -> Date? in
|
||||||
|
var components = calendar.dateComponents([.year, .month], from: date)
|
||||||
|
components.day = day
|
||||||
|
return calendar.date(from: components)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstDayOfMonth(date: Date) -> Int {
|
||||||
|
let components = calendar.dateComponents([.year, .month], from: date)
|
||||||
|
guard let firstDay = calendar.date(from: components) else { return 0 }
|
||||||
|
return calendar.component(.weekday, from: firstDay) - calendar.firstWeekday
|
||||||
|
}
|
||||||
|
|
||||||
|
func daySymbol(index: Int) -> String {
|
||||||
|
dateFormatter.shortWeekdaySymbols[(index + calendar.firstWeekday - 1) % 7]
|
||||||
|
}
|
||||||
|
|
||||||
|
func startOfMonth(date: Date) -> Date {
|
||||||
|
let components = calendar.dateComponents([.year, .month], from: date)
|
||||||
|
return calendar.date(from: components) ?? date
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PaymentCalendarView_Previews: PreviewProvider {
|
struct PaymentCalendarView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
PaymentCalendarView()
|
PaymentCalendarView(subs: .constant([
|
||||||
|
Subscription(name: "Test", payments: [Payment(amount: 9.99, intervall: .monthly, startDate: Date())], color: .blue),
|
||||||
|
Subscription(name: "Fitness First", payments: [
|
||||||
|
Payment(amount: 7.9, intervall: .weekly, startDate: Date()),
|
||||||
|
Payment(amount: 29, intervall: .quarter, startDate: Date())
|
||||||
|
], color: .red)
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user