diff --git a/ElegantCalendar.xcassets/brilliantViolet.colorset/Contents.json b/ElegantCalendar.xcassets/brilliantViolet.colorset/Contents.json new file mode 100644 index 0000000..3e606b8 --- /dev/null +++ b/ElegantCalendar.xcassets/brilliantViolet.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.573", + "green" : "0.227", + "red" : "0.271" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElegantCalendar.xcassets/craftBrown.colorset/Contents.json b/ElegantCalendar.xcassets/craftBrown.colorset/Contents.json new file mode 100644 index 0000000..899f8b3 --- /dev/null +++ b/ElegantCalendar.xcassets/craftBrown.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.388", + "green" : "0.533", + "red" : "0.659" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElegantCalendar.xcassets/fluorescentPink.colorset/Contents.json b/ElegantCalendar.xcassets/fluorescentPink.colorset/Contents.json new file mode 100644 index 0000000..dbfebe4 --- /dev/null +++ b/ElegantCalendar.xcassets/fluorescentPink.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.302", + "green" : "0.086", + "red" : "0.725" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElegantCalendar.xcassets/kiwiGreen.colorset/Contents.json b/ElegantCalendar.xcassets/kiwiGreen.colorset/Contents.json new file mode 100644 index 0000000..a63c54e --- /dev/null +++ b/ElegantCalendar.xcassets/kiwiGreen.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.161", + "green" : "0.557", + "red" : "0.459" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElegantCalendar.xcassets/mauvePurple.colorset/Contents.json b/ElegantCalendar.xcassets/mauvePurple.colorset/Contents.json new file mode 100644 index 0000000..94c8d02 --- /dev/null +++ b/ElegantCalendar.xcassets/mauvePurple.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.451", + "green" : "0.165", + "red" : "0.580" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElegantCalendar.xcassets/orangeYellow.colorset/Contents.json b/ElegantCalendar.xcassets/orangeYellow.colorset/Contents.json new file mode 100644 index 0000000..39cff7d --- /dev/null +++ b/ElegantCalendar.xcassets/orangeYellow.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.161", + "green" : "0.529", + "red" : "0.859" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElegantCalendar.xcassets/red.colorset/Contents.json b/ElegantCalendar.xcassets/red.colorset/Contents.json new file mode 100644 index 0000000..cb846c7 --- /dev/null +++ b/ElegantCalendar.xcassets/red.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.110", + "green" : "0.125", + "red" : "0.694" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElegantCalendar.xcassets/royalBlue.colorset/Contents.json b/ElegantCalendar.xcassets/royalBlue.colorset/Contents.json new file mode 100644 index 0000000..58af0a1 --- /dev/null +++ b/ElegantCalendar.xcassets/royalBlue.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.584", + "green" : "0.325", + "red" : "0.094" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 09c7f8e..4239cdd 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ 1E029CB424A4450200A81FFD /* ExampleMonthlyCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E029CB124A4450200A81FFD /* ExampleMonthlyCalendarView.swift */; }; 1E029CB524A4450200A81FFD /* ExampleYearlyCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E029CB224A4450200A81FFD /* ExampleYearlyCalendarView.swift */; }; 1E029D3224A4492B00A81FFD /* ElegantPages in Frameworks */ = {isa = PBXBuildFile; productRef = 1E029D3124A4492B00A81FFD /* ElegantPages */; }; - 1E029D3424A44C3700A81FFD /* Color+BlackPearl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E029D3324A44C3700A81FFD /* Color+BlackPearl.swift */; }; 1E628BED24A451B900DDD18E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E628BEC24A451B900DDD18E /* ContentView.swift */; }; 1E628BEF24A4528D00DDD18E /* ExampleSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E628BEE24A4528D00DDD18E /* ExampleSelectionView.swift */; }; 1E628BF124A457C700DDD18E /* Date+FullDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E628BF024A457C700DDD18E /* Date+FullDate.swift */; }; @@ -24,6 +23,10 @@ 1E995AF524A552BA00F436BE /* VisitsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E995AF124A552BA00F436BE /* VisitsListView.swift */; }; 1E995AF624A552BA00F436BE /* VisitPreviewConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E995AF224A552BA00F436BE /* VisitPreviewConstants.swift */; }; 1E995AF724A552BA00F436BE /* VisitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E995AF324A552BA00F436BE /* VisitCell.swift */; }; + 1EE7C01724BE8720000573A2 /* Calendar+Buildable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE7C01624BE8720000573A2 /* Calendar+Buildable.swift */; }; + 1EE7C01924BE8EFD000573A2 /* EnvironmentKey+CalendarTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE7C01824BE8EFD000573A2 /* EnvironmentKey+CalendarTheme.swift */; }; + 1EE7C01B24BEA4B3000573A2 /* ChangeThemeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE7C01A24BEA4B3000573A2 /* ChangeThemeButton.swift */; }; + 1EE7C01D24BEA55B000573A2 /* Color+Custom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE7C01C24BEA55B000573A2 /* Color+Custom.swift */; }; 1EE7DDDA24BD6054008BA2EC /* ElegantCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE7DDB424BD6054008BA2EC /* ElegantCalendarView.swift */; }; 1EE7DDDB24BD6054008BA2EC /* ScrollBackToTodayButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE7DDB624BD6054008BA2EC /* ScrollBackToTodayButton.swift */; }; 1EE7DDDC24BD6054008BA2EC /* MonthlyCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE7DDB824BD6054008BA2EC /* MonthlyCalendarView.swift */; }; @@ -67,7 +70,6 @@ 1E029CB024A4450200A81FFD /* ExampleCalendarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleCalendarView.swift; sourceTree = ""; }; 1E029CB124A4450200A81FFD /* ExampleMonthlyCalendarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleMonthlyCalendarView.swift; sourceTree = ""; }; 1E029CB224A4450200A81FFD /* ExampleYearlyCalendarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleYearlyCalendarView.swift; sourceTree = ""; }; - 1E029D3324A44C3700A81FFD /* Color+BlackPearl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+BlackPearl.swift"; sourceTree = ""; }; 1E628BEC24A451B900DDD18E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 1E628BEE24A4528D00DDD18E /* ExampleSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleSelectionView.swift; sourceTree = ""; }; 1E628BF024A457C700DDD18E /* Date+FullDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+FullDate.swift"; sourceTree = ""; }; @@ -75,6 +77,10 @@ 1E995AF124A552BA00F436BE /* VisitsListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VisitsListView.swift; sourceTree = ""; }; 1E995AF224A552BA00F436BE /* VisitPreviewConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VisitPreviewConstants.swift; sourceTree = ""; }; 1E995AF324A552BA00F436BE /* VisitCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VisitCell.swift; sourceTree = ""; }; + 1EE7C01624BE8720000573A2 /* Calendar+Buildable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Calendar+Buildable.swift"; sourceTree = ""; }; + 1EE7C01824BE8EFD000573A2 /* EnvironmentKey+CalendarTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EnvironmentKey+CalendarTheme.swift"; sourceTree = ""; }; + 1EE7C01A24BEA4B3000573A2 /* ChangeThemeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeThemeButton.swift; sourceTree = ""; }; + 1EE7C01C24BEA55B000573A2 /* Color+Custom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Custom.swift"; sourceTree = ""; }; 1EE7DDB424BD6054008BA2EC /* ElegantCalendarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElegantCalendarView.swift; sourceTree = ""; }; 1EE7DDB624BD6054008BA2EC /* ScrollBackToTodayButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollBackToTodayButton.swift; sourceTree = ""; }; 1EE7DDB824BD6054008BA2EC /* MonthlyCalendarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonthlyCalendarView.swift; sourceTree = ""; }; @@ -145,7 +151,7 @@ 1E029D3624A44CBC00A81FFD /* Individual Views */, 1E029D3724A44CF700A81FFD /* Calendar with Accessory View */, 1E029D3524A44CA700A81FFD /* Selection and Exit */, - 1E029D3824A44D2D00A81FFD /* Extensions */, + 1E029D3824A44D2D00A81FFD /* Shared */, 1E029C7C24A4448A00A81FFD /* Assets.xcassets */, 1E029C8124A4448A00A81FFD /* LaunchScreen.storyboard */, 1E029C8424A4448A00A81FFD /* Info.plist */, @@ -192,13 +198,14 @@ path = "Calendar with Accessory View"; sourceTree = ""; }; - 1E029D3824A44D2D00A81FFD /* Extensions */ = { + 1E029D3824A44D2D00A81FFD /* Shared */ = { isa = PBXGroup; children = ( - 1E029D3324A44C3700A81FFD /* Color+BlackPearl.swift */, 1E628BF024A457C700DDD18E /* Date+FullDate.swift */, + 1EE7C01A24BEA4B3000573A2 /* ChangeThemeButton.swift */, + 1EE7C01C24BEA55B000573A2 /* Color+Custom.swift */, ); - path = Extensions; + path = Shared; sourceTree = ""; }; 1EE7DDB124BD6054008BA2EC /* Sources */ = { @@ -296,6 +303,7 @@ 1EE7DDCB24BD6054008BA2EC /* Protocols */ = { isa = PBXGroup; children = ( + 1EE7C01624BE8720000573A2 /* Calendar+Buildable.swift */, 1EE7DDCC24BD6054008BA2EC /* ElegantCalendarCommunicator.swift */, 1EE7DDCD24BD6054008BA2EC /* ElegantCalendarDataSource.swift */, 1EE7DDCE24BD6054008BA2EC /* ElegantCalendarDelegate.swift */, @@ -314,6 +322,7 @@ 1EE7DDD524BD6054008BA2EC /* Color+CustomColors.swift */, 1EE7DDD624BD6054008BA2EC /* Enumeration+Matching.swift */, 1EE7DDD724BD6054008BA2EC /* Calender+Dates.swift */, + 1EE7C01824BE8EFD000573A2 /* EnvironmentKey+CalendarTheme.swift */, ); path = Extensions; sourceTree = ""; @@ -404,13 +413,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1EE7C01D24BEA55B000573A2 /* Color+Custom.swift in Sources */, 1E029CB424A4450200A81FFD /* ExampleMonthlyCalendarView.swift in Sources */, 1EE7DDE024BD6054008BA2EC /* SmallMonthView.swift in Sources */, 1EE7DDE624BD6054008BA2EC /* CalendarConfiguration.swift in Sources */, 1E995AF424A552BA00F436BE /* Visit.swift in Sources */, 1EE7DDE224BD6054008BA2EC /* SmallDayView.swift in Sources */, 1E029C7724A4448A00A81FFD /* AppDelegate.swift in Sources */, + 1EE7C01724BE8720000573A2 /* Calendar+Buildable.swift in Sources */, 1EE7DDDA24BD6054008BA2EC /* ElegantCalendarView.swift in Sources */, + 1EE7C01B24BEA4B3000573A2 /* ChangeThemeButton.swift in Sources */, 1EE7DDEA24BD6054008BA2EC /* MonthlyCalendarManager.swift in Sources */, 1EE7DDDC24BD6054008BA2EC /* MonthlyCalendarView.swift in Sources */, 1EE7DDE924BD6054008BA2EC /* PagerState.swift in Sources */, @@ -420,7 +432,6 @@ 1EE7DDF624BD6054008BA2EC /* LightDarkThemePreview.swift in Sources */, 1EE7DDDE24BD6054008BA2EC /* WeekView.swift in Sources */, 1E029C7924A4448A00A81FFD /* SceneDelegate.swift in Sources */, - 1E029D3424A44C3700A81FFD /* Color+BlackPearl.swift in Sources */, 1EE7DDEB24BD6054008BA2EC /* ElegantCalendarCommunicator.swift in Sources */, 1E995AF624A552BA00F436BE /* VisitPreviewConstants.swift in Sources */, 1EE7DDE424BD6054008BA2EC /* YearView.swift in Sources */, @@ -442,6 +453,7 @@ 1EE7DDF324BD6054008BA2EC /* Color+CustomColors.swift in Sources */, 1EE7DDDB24BD6054008BA2EC /* ScrollBackToTodayButton.swift in Sources */, 1EE7DDF224BD6054008BA2EC /* Date+toString.swift in Sources */, + 1EE7C01924BE8EFD000573A2 /* EnvironmentKey+CalendarTheme.swift in Sources */, 1EE7DDDF24BD6054008BA2EC /* DayView.swift in Sources */, 1EE7DDF024BD6054008BA2EC /* UIImage+BundleInit.swift in Sources */, 1EE7DDEC24BD6054008BA2EC /* ElegantCalendarDataSource.swift in Sources */, @@ -646,7 +658,7 @@ repositoryURL = "https://github.com/ThasianX/ElegantPages"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 1.0.0; + minimumVersion = 1.2.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index e61ce66..a74187d 100644 --- a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/ThasianX/ElegantPages", "state": { "branch": null, - "revision": "450ed57eed98174448929918342889b255c664d1", - "version": "1.1.0" + "revision": "6c356456458b7bf99472577d57cc2560fc3601e2", + "version": "1.2.0" } } ] diff --git a/Example/Example/Calendar with Accessory View/ExampleCalendarView.swift b/Example/Example/Calendar with Accessory View/ExampleCalendarView.swift index 941996f..d77af6e 100644 --- a/Example/Example/Calendar with Accessory View/ExampleCalendarView.swift +++ b/Example/Example/Calendar with Accessory View/ExampleCalendarView.swift @@ -8,12 +8,13 @@ struct ExampleCalendarView: View { let visitsByDay: [Date: [Visit]] + @State private var calendarTheme: CalendarTheme = .royalBlue + init(ascVisits: [Visit]) { let configuration = CalendarConfiguration( calendar: currentCalendar, startDate: ascVisits.first!.arrivalDate, - endDate: ascVisits.last!.arrivalDate, - themeColor: .blackPearl) + endDate: ascVisits.last!.arrivalDate) calendarManager = ElegantCalendarManager( configuration: configuration, @@ -28,7 +29,19 @@ struct ExampleCalendarView: View { } var body: some View { - ElegantCalendarView(calendarManager: calendarManager) + ZStack { + ElegantCalendarView(calendarManager: calendarManager) + .theme(calendarTheme) + VStack { + Spacer() + changeThemeButton + .padding(.bottom, 50) + } + } + } + + private var changeThemeButton: some View { + ChangeThemeButton(calendarTheme: $calendarTheme) } } diff --git a/Example/Example/Calendar with Accessory View/Visit.swift b/Example/Example/Calendar with Accessory View/Visit.swift index d6fb62f..07035d3 100644 --- a/Example/Example/Calendar with Accessory View/Visit.swift +++ b/Example/Example/Calendar with Accessory View/Visit.swift @@ -20,7 +20,7 @@ struct Visit { extension Visit: Identifiable { var id: Int { - locationName.hashValue + UUID().hashValue } } @@ -69,7 +69,7 @@ private extension Calendar { } -fileprivate let colorAssortment: [Color] = [.red, .green, .blue, .gray, .orange, .yellow] +fileprivate let colorAssortment: [Color] = [.turquoise, .forestGreen, .darkPink, .darkRed, .lightBlue, .salmon, .military] private extension Color { @@ -79,3 +79,23 @@ private extension Color { } } + +private extension Color { + + static let turquoise = Color(red: 24, green: 147, blue: 120) + static let forestGreen = Color(red: 22, green: 128, blue: 83) + static let darkPink = Color(red: 179, green: 102, blue: 159) + static let darkRed = Color(red: 185, green: 22, blue: 77) + static let lightBlue = Color(red: 72, green: 147, blue: 175) + static let salmon = Color(red: 219, green: 135, blue: 41) + static let military = Color(red: 117, green: 142, blue: 41) + +} + +fileprivate extension Color { + + init(red: Int, green: Int, blue: Int) { + self.init(red: Double(red)/255, green: Double(green)/255, blue: Double(blue)/255) + } + +} diff --git a/Example/Example/Individual Views/ExampleMonthlyCalendarView.swift b/Example/Example/Individual Views/ExampleMonthlyCalendarView.swift index 636fff4..5700c18 100644 --- a/Example/Example/Individual Views/ExampleMonthlyCalendarView.swift +++ b/Example/Example/Individual Views/ExampleMonthlyCalendarView.swift @@ -8,11 +8,12 @@ struct ExampleMonthlyCalendarView: View { let visitsByDay: [Date: [Visit]] + @State private var calendarTheme: CalendarTheme = .royalBlue + init(ascVisits: [Visit]) { let configuration = CalendarConfiguration(calendar: currentCalendar, startDate: ascVisits.first!.arrivalDate, - endDate: ascVisits.last!.arrivalDate, - themeColor: .blackPearl) + endDate: ascVisits.last!.arrivalDate) calendarManager = MonthlyCalendarManager(configuration: configuration, initialMonth: .daysFromToday(30)) visitsByDay = Dictionary(grouping: ascVisits, by: { currentCalendar.startOfDay(for: $0.arrivalDate) }) @@ -22,7 +23,19 @@ struct ExampleMonthlyCalendarView: View { } var body: some View { - MonthlyCalendarView(calendarManager: calendarManager) + ZStack { + MonthlyCalendarView(calendarManager: calendarManager) + .theme(calendarTheme) + VStack { + Spacer() + changeThemeButton + .padding(.bottom, 50) + } + } + } + + private var changeThemeButton: some View { + ChangeThemeButton(calendarTheme: $calendarTheme) } } diff --git a/Example/Example/Individual Views/ExampleYearlyCalendarView.swift b/Example/Example/Individual Views/ExampleYearlyCalendarView.swift index f7fb65f..af049b1 100644 --- a/Example/Example/Individual Views/ExampleYearlyCalendarView.swift +++ b/Example/Example/Individual Views/ExampleYearlyCalendarView.swift @@ -8,11 +8,12 @@ struct ExampleYearlyCalendarView: View { let visitsByDay: [Date: [Visit]] + @State private var calendarTheme: CalendarTheme = .royalBlue + init(ascVisits: [Visit]) { let configuration = CalendarConfiguration(calendar: currentCalendar, startDate: ascVisits.first!.arrivalDate, - endDate: ascVisits.last!.arrivalDate, - themeColor: .blackPearl) + endDate: ascVisits.last!.arrivalDate) calendarManager = YearlyCalendarManager(configuration: configuration, initialYear: .daysFromToday(365)) visitsByDay = Dictionary(grouping: ascVisits, by: { currentCalendar.startOfDay(for: $0.arrivalDate) }) @@ -21,7 +22,19 @@ struct ExampleYearlyCalendarView: View { } var body: some View { - YearlyCalendarView(calendarManager: calendarManager) + ZStack { + YearlyCalendarView(calendarManager: calendarManager) + .theme(calendarTheme) + VStack { + Spacer() + changeThemeButton + .padding(.bottom, 50) + } + } + } + + private var changeThemeButton: some View { + ChangeThemeButton(calendarTheme: $calendarTheme) } } diff --git a/Example/Example/Selection and Exit/ExampleSelectionView.swift b/Example/Example/Selection and Exit/ExampleSelectionView.swift index 2dc18bc..3af70d2 100644 --- a/Example/Example/Selection and Exit/ExampleSelectionView.swift +++ b/Example/Example/Selection and Exit/ExampleSelectionView.swift @@ -8,7 +8,8 @@ fileprivate let turnAnimation: Animation = .spring(response: 0.4, dampingFractio class SelectionModel: ObservableObject { @Published var showCalendar = false - @Published var calendarManager: ElegantCalendarManager = .init(configuration: .init(startDate: .daysFromToday(-365), endDate: .daysFromToday(365*3), themeColor: .blackPearl)) + @Published var calendarManager: ElegantCalendarManager = .init(configuration: .init(startDate: .daysFromToday(-365), + endDate: .daysFromToday(365*3))) init() { calendarManager.delegate = self diff --git a/Example/Example/Shared/ChangeThemeButton.swift b/Example/Example/Shared/ChangeThemeButton.swift new file mode 100644 index 0000000..39eb2ef --- /dev/null +++ b/Example/Example/Shared/ChangeThemeButton.swift @@ -0,0 +1,26 @@ +// Kevin Li - 7:42 PM - 7/14/20 + +import SwiftUI + +struct ChangeThemeButton: View { + + @Binding var calendarTheme: CalendarTheme + + var body: some View { + Button(action: { + self.calendarTheme = .randomTheme + }) { + Text("CHANGE THEME") + } + } + +} + +private extension CalendarTheme { + + static var randomTheme: CalendarTheme { + let randomNumber = arc4random_uniform(UInt32(CalendarTheme.allThemes.count)) + return CalendarTheme.allThemes[Int(randomNumber)] + } + +} diff --git a/Example/Example/Extensions/Color+BlackPearl.swift b/Example/Example/Shared/Color+Custom.swift similarity index 73% rename from Example/Example/Extensions/Color+BlackPearl.swift rename to Example/Example/Shared/Color+Custom.swift index cef803d..98d640a 100644 --- a/Example/Example/Extensions/Color+BlackPearl.swift +++ b/Example/Example/Shared/Color+Custom.swift @@ -1,4 +1,4 @@ -// Kevin Li - 8:06 PM - 6/24/20 +// Kevin Li - 7:45 PM - 7/14/20 import SwiftUI diff --git a/Example/Example/Extensions/Date+FullDate.swift b/Example/Example/Shared/Date+FullDate.swift similarity index 100% rename from Example/Example/Extensions/Date+FullDate.swift rename to Example/Example/Shared/Date+FullDate.swift diff --git a/Package.resolved b/Package.resolved index e61ce66..a74187d 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/ThasianX/ElegantPages", "state": { "branch": null, - "revision": "450ed57eed98174448929918342889b255c664d1", - "version": "1.1.0" + "revision": "6c356456458b7bf99472577d57cc2560fc3601e2", + "version": "1.2.0" } } ] diff --git a/Package.swift b/Package.swift index b594581..0f0aaa6 100644 --- a/Package.swift +++ b/Package.swift @@ -13,7 +13,7 @@ let package = Package( targets: ["ElegantCalendar"]), ], dependencies: [ - .package(url: "https://github.com/ThasianX/ElegantPages", .upToNextMajor(from: "1.1.0")) + .package(url: "https://github.com/ThasianX/ElegantPages", from: "1.2.0") ], targets: [ .target( diff --git a/README.md b/README.md index 1a34591..c921062 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,18 @@ ElegantCalendar is an efficient and customizable full screen calendar written in +### Comes with 8 default themes. You can also configure your own theme. Read more to find out. + +

+ + + + + + + +

+ - [Introduction](#introduction) - [Basic Usage](#basic-usage) - [How It Works](#how-it-works) @@ -43,6 +55,7 @@ Features: * Flexibility in either using the full calendar view that has both the monthly and yearly view or just one of the individual views * Haptics when performing certain actions * Intuitive navigation between the yearly and monthly view: swipe between views or tap on the month header to navigate to the yearly view +* Elegant default themes ## Basic usage @@ -57,8 +70,7 @@ struct ExampleCalendarView: View { @ObservedObject var calendarManager = ElegantCalendarManager( configuration: CalendarConfiguration(startDate: startDate, - endDate: endDate, - themeColor: .blackPearl)) + endDate: endDate)) var body: some View { ElegantCalendarView(calendarManager: calendarManager) @@ -77,8 +89,7 @@ struct ExampleMonthlyCalendarView: View { @ObservedObject var calendarManager = MonthlyCalendarManager( configuration: CalendarConfiguration(startDate: startDate, - endDate: endDate, - themeColor: .blackPearl)) + endDate: endDate)) var body: some View { MonthlyCalendarView(calendarManager: calendarManager) @@ -90,8 +101,7 @@ struct ExampleYearlyCalendarView: View { @ObservedObject var calendarManager = YearlyCalendarManager( configuration: CalendarConfiguration(startDate: startDate, - endDate: endDate, - themeColor: .blackPearl)) + endDate: endDate)) var body: some View { YearlyCalendarView(calendarManager: calendarManager) @@ -111,7 +121,7 @@ So how can this be fixed? Either create a simpler yearly calendar that doesn't r ## Customization -The following aspects of `ElegantCalendarManager` can be customized: +### `ElegantCalendarManager` #### `configuration`: The configuration of the calendar view @@ -121,10 +131,8 @@ public struct CalendarConfiguration: Equatable { let calendar: Calendar let ascending: Bool // reverses the order in which the calendar is laid out - let allowHaptics: Bool let startDate: Date let endDate: Date - let themeColor: Color // if you want to support light and dark mode, make sure your color does so as well } @@ -176,6 +184,52 @@ public protocol YearlyCalendarDelegate { This is just a convenience to handle the shortcomings of the `@Published` wrapper which doesn't support `didSet`. Conform to this if you need to do things when a month is displayed or date changes. + +### `ElegantCalendarView` & `YearlyCalendarView` & `MonthlyCalendarView` + +#### `theme`: The theme of various components of the calendar. Default is royal blue. + +```swift + +public struct CalendarTheme: Equatable, Hashable { + + let primary: Color + +} + +public extension CalendarTheme { + + static let brilliantViolet = CalendarTheme(primary: .brilliantViolet) + static let craftBrown = CalendarTheme(primary: .craftBrown) + static let fluorescentPink = CalendarTheme(primary: .fluorescentPink) + static let kiwiGreen = CalendarTheme(primary: .kiwiGreen) + static let mauvePurple = CalendarTheme(primary: .mauvePurple) + static let orangeYellow = CalendarTheme(primary: .orangeYellow) + static let red = CalendarTheme(primary: .red) + static let royalBlue = CalendarTheme(primary: .royalBlue) + +} + +ElegantCalendarView(...) + .theme(.mauvePurple) + +``` + +To configure your own theme, just pass in your color into the `CalendarTheme` initializer. To have dynamic appearance, make sure your `Color` has both a light and dark appearance. + +### `ElegantCalendarView` & `MonthlyCalendarView` + +#### `allowsHaptics`: Whether haptics is enabled or not. Default is enabled. + +```swift + +ElegantCalendarView(...) + .allowsHaptics(false) + +``` + +Users get haptics whenever they tap a day, scroll to a new month, or press the scroll back to today button. + ## Use Cases The following aspects of `ElegantCalendarManager` can be used: @@ -209,7 +263,7 @@ If you are using `Package.swift`, you can also add `ElegantCalendar` as a depend let package = Package( name: "TestProject", dependencies: [ - .package(url: "https://github.com/ThasianX/ElegantCalendar", from: "3.0.0") + .package(url: "https://github.com/ThasianX/ElegantCalendar", from: "4.0.0") ], targets: [ .target(name: "TestProject", dependencies: ["ElegantCalendar"]) diff --git a/Screenshots/brilliantViolet.PNG b/Screenshots/brilliantViolet.PNG new file mode 100644 index 0000000..8e180e8 Binary files /dev/null and b/Screenshots/brilliantViolet.PNG differ diff --git a/Screenshots/craftBrown.PNG b/Screenshots/craftBrown.PNG new file mode 100644 index 0000000..38ae585 Binary files /dev/null and b/Screenshots/craftBrown.PNG differ diff --git a/Screenshots/fluorescentPink.PNG b/Screenshots/fluorescentPink.PNG new file mode 100644 index 0000000..ce0696f Binary files /dev/null and b/Screenshots/fluorescentPink.PNG differ diff --git a/Screenshots/kiwiGreen.PNG b/Screenshots/kiwiGreen.PNG new file mode 100644 index 0000000..b28d171 Binary files /dev/null and b/Screenshots/kiwiGreen.PNG differ diff --git a/Screenshots/mauvePurple.PNG b/Screenshots/mauvePurple.PNG new file mode 100644 index 0000000..1198dc2 Binary files /dev/null and b/Screenshots/mauvePurple.PNG differ diff --git a/Screenshots/orangeYellow.PNG b/Screenshots/orangeYellow.PNG new file mode 100644 index 0000000..ca913fb Binary files /dev/null and b/Screenshots/orangeYellow.PNG differ diff --git a/Screenshots/red.PNG b/Screenshots/red.PNG new file mode 100644 index 0000000..1dcfbee Binary files /dev/null and b/Screenshots/red.PNG differ diff --git a/Screenshots/royalblue.PNG b/Screenshots/royalblue.PNG new file mode 100644 index 0000000..f14447d Binary files /dev/null and b/Screenshots/royalblue.PNG differ diff --git a/Sources/ElegantCalendar/Helpers/Extensions/EnvironmentKey+CalendarTheme.swift b/Sources/ElegantCalendar/Helpers/Extensions/EnvironmentKey+CalendarTheme.swift new file mode 100644 index 0000000..6e73edb --- /dev/null +++ b/Sources/ElegantCalendar/Helpers/Extensions/EnvironmentKey+CalendarTheme.swift @@ -0,0 +1,62 @@ +// Kevin Li - 6:10 PM - 7/14/20 + +import SwiftUI + +public struct CalendarTheme: Equatable, Hashable { + + public let primary: Color + + public init(primary: Color) { + self.primary = primary + } + +} + +public extension CalendarTheme { + + static let allThemes: [CalendarTheme] = [.brilliantViolet, .craftBrown, .fluorescentPink, .kiwiGreen, .mauvePurple, .orangeYellow, .red, .royalBlue] + + static let brilliantViolet = CalendarTheme(primary: .brilliantViolet) + static let craftBrown = CalendarTheme(primary: .craftBrown) + static let fluorescentPink = CalendarTheme(primary: .fluorescentPink) + static let kiwiGreen = CalendarTheme(primary: .kiwiGreen) + static let mauvePurple = CalendarTheme(primary: .mauvePurple) + static let orangeYellow = CalendarTheme(primary: .orangeYellow) + static let red = CalendarTheme(primary: .red) + static let royalBlue = CalendarTheme(primary: .royalBlue) + +} + +extension CalendarTheme { + + static let `default`: CalendarTheme = .royalBlue + +} + +struct CalendarThemeKey: EnvironmentKey { + + static let defaultValue: CalendarTheme = .default + +} + +extension EnvironmentValues { + + var calendarTheme: CalendarTheme { + get { self[CalendarThemeKey.self] } + set { self[CalendarThemeKey.self] = newValue } + } + +} + +private extension Color { + + static let brilliantViolet = Color("brilliantViolet") + static let craftBrown = Color("craftBrown") + static let fluorescentPink = Color("fluorescentPink") + static let kiwiGreen = Color("kiwiGreen") + static let mauvePurple = Color("mauvePurple") + static let orangeYellow = Color("orangeYellow") + static let red = Color("red") + static let royalBlue = Color("royalBlue") + +} diff --git a/Sources/ElegantCalendar/Helpers/Models/CalendarConfiguration.swift b/Sources/ElegantCalendar/Helpers/Models/CalendarConfiguration.swift index 2ffc2c5..549dd0c 100644 --- a/Sources/ElegantCalendar/Helpers/Models/CalendarConfiguration.swift +++ b/Sources/ElegantCalendar/Helpers/Models/CalendarConfiguration.swift @@ -2,22 +2,19 @@ import SwiftUI +/// Any changes to the configuration will reset the calendar based on its new settings public struct CalendarConfiguration: Equatable { public let calendar: Calendar public let ascending: Bool - public let allowHaptics: Bool public let startDate: Date public let endDate: Date - public let themeColor: Color - public init(calendar: Calendar = .current, ascending: Bool = true, allowHaptics: Bool = true, startDate: Date, endDate: Date, themeColor: Color) { + public init(calendar: Calendar = .current, ascending: Bool = true, startDate: Date, endDate: Date) { self.calendar = calendar self.ascending = ascending - self.allowHaptics = allowHaptics self.startDate = startDate self.endDate = endDate - self.themeColor = themeColor } var referenceDate: Date { @@ -30,8 +27,7 @@ extension CalendarConfiguration { static let mock = CalendarConfiguration( startDate: .daysFromToday(-365*2), - endDate: .daysFromToday(365*2), - themeColor: .purple) + endDate: .daysFromToday(365*2)) } @@ -47,10 +43,6 @@ extension ConfigurationDirectAccess { configuration.calendar } - var allowHaptics: Bool { - configuration.allowHaptics - } - var startDate: Date { configuration.startDate } @@ -59,10 +51,6 @@ extension ConfigurationDirectAccess { configuration.endDate } - var themeColor: Color { - configuration.themeColor - } - var referenceDate: Date { configuration.referenceDate } diff --git a/Sources/ElegantCalendar/Helpers/Models/ObservableObjects/MonthlyCalendarManager.swift b/Sources/ElegantCalendar/Helpers/Models/ObservableObjects/MonthlyCalendarManager.swift index 0e233cd..03bc82e 100644 --- a/Sources/ElegantCalendar/Helpers/Models/ObservableObjects/MonthlyCalendarManager.swift +++ b/Sources/ElegantCalendar/Helpers/Models/ObservableObjects/MonthlyCalendarManager.swift @@ -19,8 +19,11 @@ public class MonthlyCalendarManager: ObservableObject, ConfigurationDirectAccess public let configuration: CalendarConfiguration public let months: [Date] + var allowsHaptics: Bool = true private var isHapticActive: Bool = true + var theme: CalendarTheme = .default + private var anyCancellable: AnyCancellable? public init(configuration: CalendarConfiguration, initialMonth: Date? = nil) { @@ -43,6 +46,7 @@ public class MonthlyCalendarManager: ObservableObject, ConfigurationDirectAccess pagerManager = .init(startingPage: startingPage, pageCount: months.count, pageTurnType: .monthlyEarlyCutoff) + pagerManager.datasource = self pagerManager.delegate = self @@ -57,6 +61,7 @@ extension MonthlyCalendarManager: ElegantPagesDataSource { public func elegantPages(viewForPage page: Int) -> AnyView { MonthView(calendarManager: self, month: months[page]) + .environment(\.calendarTheme, theme) .erased } @@ -71,7 +76,7 @@ extension MonthlyCalendarManager: ElegantPagesDelegate { delegate?.calendar(willDisplayMonth: currentMonth) - if allowHaptics && isHapticActive { + if allowsHaptics && isHapticActive { UIImpactFeedbackGenerator.generateSelectionHaptic() } else { isHapticActive = true @@ -83,30 +88,48 @@ extension MonthlyCalendarManager: ElegantPagesDelegate { extension MonthlyCalendarManager { - public func scrollBackToToday() { + @discardableResult + public func scrollBackToToday() -> Bool { scrollToDay(Date()) } - public func scrollToDay(_ day: Date, animated: Bool = true) { - scrollToMonth(day, animated: animated) - if datasource?.calendar(canSelectDate: day) ?? true { + @discardableResult + public func scrollToDay(_ day: Date, animated: Bool = true) -> Bool { + let didScrollToMonth = scrollToMonth(day, animated: animated) + let canSelectDay = datasource?.calendar(canSelectDate: day) ?? true + + if canSelectDay { DispatchQueue.main.asyncAfter(deadline: .now()+0.15) { - self.dayTapped(day: day) + self.dayTapped(day: day, withHaptic: !didScrollToMonth) } } + + return canSelectDay } - func dayTapped(day: Date) { + func dayTapped(day: Date, withHaptic: Bool) { + if allowsHaptics && withHaptic { + UIImpactFeedbackGenerator.generateSelectionHaptic() + } + selectedDate = day delegate?.calendar(didSelectDay: day) } - public func scrollToMonth(_ month: Date, animated: Bool = true) { + @discardableResult + public func scrollToMonth(_ month: Date, animated: Bool = true) -> Bool { isHapticActive = animated - if !calendar.isDate(currentMonth, equalTo: month, toGranularities: [.month, .year]) { + + let needsToScroll = !calendar.isDate(currentMonth, equalTo: month, toGranularities: [.month, .year]) + + if needsToScroll { let page = calendar.monthsBetween(referenceDate, and: month) pagerManager.scroll(to: page, animated: animated) + } else { + isHapticActive = true } + + return needsToScroll } } diff --git a/Sources/ElegantCalendar/Helpers/Models/Protocols/Calendar+Buildable.swift b/Sources/ElegantCalendar/Helpers/Models/Protocols/Calendar+Buildable.swift new file mode 100644 index 0000000..fb8faf2 --- /dev/null +++ b/Sources/ElegantCalendar/Helpers/Models/Protocols/Calendar+Buildable.swift @@ -0,0 +1,72 @@ +// Kevin Li - 5:36 PM - 7/14/20 + +import Foundation +import SwiftUI + +/// Adds a helper function to mutate a properties and help implement _Builder_ pattern +protocol Buildable { } + +extension Buildable { + + /// Mutates a property of the instance + /// + /// - Parameter keyPath: `WritableKeyPath` to the instance property to be modified + /// - Parameter value: value to overwrite the instance property + func mutating(keyPath: WritableKeyPath, value: T) -> Self { + var newSelf = self + newSelf[keyPath: keyPath] = value + return newSelf + } + +} + +extension MonthlyCalendarView: Buildable { + + /// Changes the theme of the calendar + /// + /// - Parameter theme: theme of various components of the calendar + public func theme(_ theme: CalendarTheme) -> Self { + calendarManager.theme = theme + calendarManager.pagerManager.reloadPages() + return mutating(keyPath: \.theme, value: theme) + } + + /// Sets whether haptics is enabled or not + /// + /// - Parameter value: `true` if haptics is allowed, `false`, otherwise. Defaults to `true` + public func allowsHaptics(_ value: Bool = true) -> Self { + calendarManager.allowsHaptics = value + return self + } + +} + +extension ElegantCalendarView: Buildable { + + /// Changes the theme of the calendar + /// + /// - Parameter theme: theme of various components of the calendar + public func theme(_ theme: CalendarTheme) -> Self { + mutating(keyPath: \.theme, value: theme) + } + + /// Sets whether haptics is enabled or not + /// + /// - Parameter value: `true` if haptics is allowed, `false`, otherwise. Defaults to `true` + public func allowsHaptics(_ value: Bool = true) -> Self { + calendarManager.monthlyManager.allowsHaptics = value + return self + } + +} + +extension YearlyCalendarView: Buildable { + + /// Changes the theme of the calendar + /// + /// - Parameter theme: theme of various components of the calendar + public func theme(_ theme: CalendarTheme) -> Self { + mutating(keyPath: \.theme, value: theme) + } + +} diff --git a/Sources/ElegantCalendar/Views/ElegantCalendarView.swift b/Sources/ElegantCalendar/Views/ElegantCalendarView.swift index 074b24d..dce54e3 100644 --- a/Sources/ElegantCalendar/Views/ElegantCalendarView.swift +++ b/Sources/ElegantCalendar/Views/ElegantCalendarView.swift @@ -5,6 +5,8 @@ import SwiftUI public struct ElegantCalendarView: View { + var theme: CalendarTheme = .default + @ObservedObject public var calendarManager: ElegantCalendarManager public init(calendarManager: ElegantCalendarManager) { @@ -20,10 +22,12 @@ public struct ElegantCalendarView: View { private var yearlyCalendarView: some View { YearlyCalendarView(calendarManager: calendarManager.yearlyManager) + .theme(theme) } private var monthlyCalendarView: some View { MonthlyCalendarView(calendarManager: calendarManager.monthlyManager) + .theme(theme) } } diff --git a/Sources/ElegantCalendar/Views/Monthly/DayView.swift b/Sources/ElegantCalendar/Views/Monthly/DayView.swift index 742e903..81a1549 100644 --- a/Sources/ElegantCalendar/Views/Monthly/DayView.swift +++ b/Sources/ElegantCalendar/Views/Monthly/DayView.swift @@ -4,6 +4,8 @@ import SwiftUI struct DayView: View, MonthlyCalendarManagerDirectAccess { + @Environment(\.calendarTheme) var theme: CalendarTheme + @ObservedObject var calendarManager: MonthlyCalendarManager let week: Date @@ -52,7 +54,7 @@ struct DayView: View, MonthlyCalendarManagerDirectAccess { private var foregroundColor: Color { if isDayToday { - return themeColor + return theme.primary } else { return .primary } @@ -63,7 +65,7 @@ struct DayView: View, MonthlyCalendarManagerDirectAccess { if isDayToday { Color.primary } else if isDaySelectableAndInRange { - themeColor + theme.primary .opacity(datasource?.calendar(backgroundColorOpacityForDate: day) ?? 1) } else { Color.clear @@ -80,11 +82,7 @@ struct DayView: View, MonthlyCalendarManagerDirectAccess { guard isDayWithinDateRange && canSelectDay else { return } if isDayToday || isDayWithinWeekMonthAndYear { - if configuration.allowHaptics { - UIImpactFeedbackGenerator.generateSelectionHaptic() - } - - calendarManager.dayTapped(day: day) + calendarManager.dayTapped(day: day, withHaptic: true) } } diff --git a/Sources/ElegantCalendar/Views/Monthly/MonthView.swift b/Sources/ElegantCalendar/Views/Monthly/MonthView.swift index 8e76772..9a23205 100644 --- a/Sources/ElegantCalendar/Views/Monthly/MonthView.swift +++ b/Sources/ElegantCalendar/Views/Monthly/MonthView.swift @@ -5,7 +5,9 @@ import SwiftUI fileprivate let daysOfWeekInitials = ["S", "M", "T", "W", "T", "F", "S"] struct MonthView: View, MonthlyCalendarManagerDirectAccess { - + + @Environment(\.calendarTheme) var theme: CalendarTheme + @ObservedObject var calendarManager: MonthlyCalendarManager let month: Date @@ -59,14 +61,14 @@ private extension MonthView { .font(.system(size: 26)) .bold() .tracking(7) - .foregroundColor(isWithinSameMonthAndYearAsToday ? themeColor : .primary) + .foregroundColor(isWithinSameMonthAndYearAsToday ? theme.primary : .primary) } var yearText: some View { Text(month.year) .font(.system(size: 12)) .tracking(2) - .foregroundColor(isWithinSameMonthAndYearAsToday ? themeColor : .gray) + .foregroundColor(isWithinSameMonthAndYearAsToday ? theme.primary : .gray) .opacity(0.95) } diff --git a/Sources/ElegantCalendar/Views/Monthly/MonthlyCalendarView.swift b/Sources/ElegantCalendar/Views/Monthly/MonthlyCalendarView.swift index 9a3a391..102d105 100644 --- a/Sources/ElegantCalendar/Views/Monthly/MonthlyCalendarView.swift +++ b/Sources/ElegantCalendar/Views/Monthly/MonthlyCalendarView.swift @@ -5,6 +5,8 @@ import SwiftUI public struct MonthlyCalendarView: View, MonthlyCalendarManagerDirectAccess { + var theme: CalendarTheme = .default + @ObservedObject public var calendarManager: MonthlyCalendarManager private var isTodayWithinDateRange: Bool { @@ -23,13 +25,11 @@ public struct MonthlyCalendarView: View, MonthlyCalendarManagerDirectAccess { public var body: some View { ZStack(alignment: .top) { monthsList - .zIndex(0) if isTodayWithinDateRange && !isCurrentMonthYearSameAsTodayMonthYear { leftAlignedScrollBackToTodayButton .padding(.trailing, CalendarConstants.Monthly.outerHorizontalPadding) .offset(y: CalendarConstants.Monthly.topPadding + 3) .transition(.opacity) - .zIndex(1) } } } @@ -41,8 +41,8 @@ public struct MonthlyCalendarView: View, MonthlyCalendarManagerDirectAccess { private var leftAlignedScrollBackToTodayButton: some View { HStack { Spacer() - ScrollBackToTodayButton(scrollBackToToday: calendarManager.scrollBackToToday, - color: themeColor) + ScrollBackToTodayButton(scrollBackToToday: { self.calendarManager.scrollBackToToday() }, + color: theme.primary) } } diff --git a/Sources/ElegantCalendar/Views/Yearly/SmallMonthView.swift b/Sources/ElegantCalendar/Views/Yearly/SmallMonthView.swift index 28ddb37..9f959b1 100644 --- a/Sources/ElegantCalendar/Views/Yearly/SmallMonthView.swift +++ b/Sources/ElegantCalendar/Views/Yearly/SmallMonthView.swift @@ -4,6 +4,8 @@ import SwiftUI struct SmallMonthView: View, YearlyCalendarManagerDirectAccess { + @Environment(\.calendarTheme) var theme: CalendarTheme + let calendarManager: YearlyCalendarManager let month: Date @@ -44,7 +46,7 @@ struct SmallMonthView: View, YearlyCalendarManagerDirectAccess { Text(month.abbreviatedMonth.uppercased()) .font(.subheadline) .bold() - .foregroundColor(isWithinSameMonthAndYearAsToday ? themeColor : .primary) + .foregroundColor(isWithinSameMonthAndYearAsToday ? theme.primary : .primary) } private var weeksViewStack: some View { diff --git a/Sources/ElegantCalendar/Views/Yearly/YearView.swift b/Sources/ElegantCalendar/Views/Yearly/YearView.swift index 3a7fff4..afecf11 100644 --- a/Sources/ElegantCalendar/Views/Yearly/YearView.swift +++ b/Sources/ElegantCalendar/Views/Yearly/YearView.swift @@ -4,6 +4,8 @@ import SwiftUI struct YearView: View, YearlyCalendarManagerDirectAccess { + @Environment(\.calendarTheme) var theme: CalendarTheme + let calendarManager: YearlyCalendarManager let year: Date @@ -25,7 +27,7 @@ struct YearView: View, YearlyCalendarManagerDirectAccess { private var yearText: some View { Text(year.year) .font(.system(size: 38, weight: .thin, design: .rounded)) - .foregroundColor(isYearSameAsTodayYear ? themeColor : .primary) + .foregroundColor(isYearSameAsTodayYear ? theme.primary : .primary) } private var monthsStack: some View { diff --git a/Sources/ElegantCalendar/Views/Yearly/YearlyCalendarView.swift b/Sources/ElegantCalendar/Views/Yearly/YearlyCalendarView.swift index 0308222..6085afd 100644 --- a/Sources/ElegantCalendar/Views/Yearly/YearlyCalendarView.swift +++ b/Sources/ElegantCalendar/Views/Yearly/YearlyCalendarView.swift @@ -4,6 +4,8 @@ import SwiftUI public struct YearlyCalendarView: View, YearlyCalendarManagerDirectAccess { + var theme: CalendarTheme = .default + @ObservedObject public var calendarManager: YearlyCalendarManager private var isTodayWithinDateRange: Bool { @@ -37,6 +39,7 @@ public struct YearlyCalendarView: View, YearlyCalendarManagerDirectAccess { YearlyCalendarScrollView(calendarManager: calendarManager) { ForEach(self.years, id: \.self) { year in YearView(calendarManager: self.calendarManager, year: year) + .environment(\.calendarTheme, self.theme) } } .frame(width: CalendarConstants.cellWidth, @@ -45,14 +48,14 @@ public struct YearlyCalendarView: View, YearlyCalendarManagerDirectAccess { private var scrollBackToTodayButton: some View { ScrollBackToTodayButton(scrollBackToToday: calendarManager.scrollBackToToday, - color: themeColor) + color: theme.primary) } } -private struct YearlyCalendarScrollView: UIViewRepresentable { +private struct YearlyCalendarScrollView: UIViewControllerRepresentable { - typealias UIViewType = UIScrollView + typealias UIViewControllerType = UIScrollViewViewController @ObservedObject var calendarManager: YearlyCalendarManager @@ -67,26 +70,18 @@ private struct YearlyCalendarScrollView: UIViewRepresentable { Coordinator(parent: self) } - func makeUIView(context: Context) -> UIScrollView { - let hosting = UIHostingController(rootView: content) - - let size = hosting.view.sizeThatFits(CGSize(width: screen.width, height: .greatestFiniteMagnitude)) - hosting.view.frame = CGRect(x: 0, y: 0, - width: screen.width, - height: size.height) - - let scrollView = UIScrollView().withPagination(delegate: context.coordinator) - scrollView.addSubview(hosting.view) - scrollView.contentSize = CGSize(width: screen.width, height: size.height) - - return scrollView + func makeUIViewController(context: Context) -> UIScrollViewViewController { + UIScrollViewViewController(content: content, delegate: context.coordinator) } - func updateUIView(_ scrollView: UIScrollView, context: Context) { + func updateUIViewController(_ viewController: UIScrollViewViewController, context: Context) { + viewController.hosting.rootView = content + switch calendarManager.currentPage.state { case .scroll: DispatchQueue.main.async { - scrollView.setContentOffset(CGPoint(x: 0, y: self.calendarManager.destinationOffset), animated: true) + viewController.scrollView.setContentOffset(CGPoint(x: 0, y: self.calendarManager.destinationOffset), + animated: true) } case .completed: () @@ -131,6 +126,54 @@ private extension YearlyCalendarManager { } +private class UIScrollViewViewController: UIViewController { + + let hosting: UIHostingController + let scrollView: UIScrollView + + init(content: AnyView, delegate: UIScrollViewDelegate) { + hosting = UIHostingController(rootView: content) + scrollView = UIScrollView().withPagination(delegate: delegate) + super.init(nibName: nil, bundle: nil) + + let size = hosting.view.sizeThatFits(CGSize(width: screen.width, height: .greatestFiniteMagnitude)) + hosting.view.frame = CGRect(x: 0, y: 0, + width: screen.width, + height: size.height) + + scrollView.addSubview(hosting.view) + scrollView.contentSize = CGSize(width: screen.width, height: size.height) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + view.addSubview(scrollView) + pinEdges(of: scrollView, to: view) + + hosting.willMove(toParent: self) + scrollView.addSubview(self.hosting.view) + pinEdges(of: hosting.view, to: scrollView) + hosting.didMove(toParent: self) + } + + func pinEdges(of viewA: UIView, to viewB: UIView) { + viewA.translatesAutoresizingMaskIntoConstraints = false + viewB.addConstraints([ + viewA.leadingAnchor.constraint(equalTo: viewB.leadingAnchor), + viewA.trailingAnchor.constraint(equalTo: viewB.trailingAnchor), + viewA.topAnchor.constraint(equalTo: viewB.topAnchor), + viewA.bottomAnchor.constraint(equalTo: viewB.bottomAnchor), + ]) + } + +} + + private extension UIScrollView { func withPagination(delegate: UIScrollViewDelegate) -> UIScrollView {