From d4c6fdea85470bab980e2409cefa22c86c2ce1b8 Mon Sep 17 00:00:00 2001 From: apramc <122586830+apramc@users.noreply.github.com> Date: Sun, 2 Mar 2025 19:39:32 -0800 Subject: [PATCH] Update DayRangeSelectionDemoViewController.swift Adding different colors and more range selections --- .../DayRangeSelectionDemoViewController.swift | 185 ++++++++---------- 1 file changed, 82 insertions(+), 103 deletions(-) diff --git a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DayRangeSelectionDemoViewController.swift b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DayRangeSelectionDemoViewController.swift index 36bf8430..a3928573 100644 --- a/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DayRangeSelectionDemoViewController.swift +++ b/Example/HorizonCalendarExample/HorizonCalendarExample/Demo View Controllers/DayRangeSelectionDemoViewController.swift @@ -1,115 +1,94 @@ -// Created by Bryan Keller on 6/18/20. -// Copyright © 2020 Airbnb Inc. All rights reserved. - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import HorizonCalendar import UIKit +import HorizonCalendar final class DayRangeSelectionDemoViewController: BaseDemoViewController { - // MARK: Internal - - override func viewDidLoad() { - super.viewDidLoad() - - title = "Day Range Selection" - - calendarView.daySelectionHandler = { [weak self] day in - guard let self else { return } - - DayRangeSelectionHelper.updateDayRange( - afterTapSelectionOf: day, - existingDayRange: &selectedDayRange) - - calendarView.setContent(makeContent()) - } - - calendarView.multiDaySelectionDragHandler = { [weak self, calendar] day, state in - guard let self else { return } - - DayRangeSelectionHelper.updateDayRange( - afterDragSelectionOf: day, - existingDayRange: &selectedDayRange, - initialDayRange: &selectedDayRangeAtStartOfDrag, - state: state, - calendar: calendar) - - calendarView.setContent(makeContent()) - } - } - - override func makeContent() -> CalendarViewContent { - let startDate = calendar.date(from: DateComponents(year: 2020, month: 01, day: 01))! - let endDate = calendar.date(from: DateComponents(year: 2021, month: 12, day: 31))! - - let dateRanges: Set> - let selectedDayRange = selectedDayRange - if - let selectedDayRange, - let lowerBound = calendar.date(from: selectedDayRange.lowerBound.components), - let upperBound = calendar.date(from: selectedDayRange.upperBound.components) - { - dateRanges = [lowerBound...upperBound] - } else { - dateRanges = [] + // Properties for storing multiple date ranges and their respective colors + private var dateRanges: [ClosedRange] = [] + private var dateRangeColors: [ClosedRange: UIColor] = [:] + private var currentRangeStart: Date? + + override func viewDidLoad() { + super.viewDidLoad() + + title = "Day Range Selection" + + calendarView.daySelectionHandler = { [weak self] day in + guard let self = self, let date = self.calendar.date(from: day.components) else { return } + + if let start = self.currentRangeStart { + // Complete the current range and prompt for a color + let newRange = start...date + self.dateRanges.append(newRange) + self.currentRangeStart = nil // Reset the start date for a new range + + self.promptForColorSelection(dateRange: newRange) { color in + self.dateRangeColors[newRange] = color + self.calendarView.setContent(self.makeContent()) + } + } else { + // Start a new range + self.currentRangeStart = date + } + } } - return CalendarViewContent( - calendar: calendar, - visibleDateRange: startDate...endDate, - monthsLayout: monthsLayout) - - .interMonthSpacing(24) - .verticalDayMargin(8) - .horizontalDayMargin(8) - - .dayItemProvider { [calendar, dayDateFormatter] day in - var invariantViewProperties = DayView.InvariantViewProperties.baseInteractive - - let isSelectedStyle: Bool - if let selectedDayRange { - isSelectedStyle = day == selectedDayRange.lowerBound || day == selectedDayRange.upperBound - } else { - isSelectedStyle = false + private func promptForColorSelection(dateRange: ClosedRange, completion: @escaping (UIColor) -> Void) { + let alert = UIAlertController(title: "Select Color", message: "Choose a color for the selected date range", preferredStyle: .actionSheet) + + let colors: [UIColor: String] = [.red: "Red", .green: "Green", .blue: "Blue", .orange: "Orange"] + for (color, name) in colors { + alert.addAction(UIAlertAction(title: name, style: .default, handler: { _ in + completion(color) + })) } + + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) + present(alert, animated: true, completion: nil) + } - if isSelectedStyle { - invariantViewProperties.backgroundShapeDrawingConfig.fillColor = .systemBackground - invariantViewProperties.backgroundShapeDrawingConfig.borderColor = UIColor(.accentColor) + override func makeContent() -> CalendarViewContent { + let calendar = Calendar.current + let startDate = calendar.date(from: DateComponents(year: 2020, month: 01, day: 01))! + let endDate = calendar.date(from: DateComponents(year: 2021, month: 12, day: 31))! + + // Map your date ranges to ClosedRange using correct component extraction + let dateRanges = self.dateRanges.map { range -> ClosedRange in + let startComponents = calendar.dateComponents([.year, .month, .day], from: range.lowerBound) + let endComponents = calendar.dateComponents([.year, .month, .day], from: range.upperBound) + let start = calendar.date(from: startComponents)! + let end = calendar.date(from: endComponents)! + return start...end } - let date = calendar.date(from: day.components) - - return DayView.calendarItemModel( - invariantViewProperties: invariantViewProperties, - content: .init( - dayText: "\(day.day)", - accessibilityLabel: date.map { dayDateFormatter.string(from: $0) }, - accessibilityHint: nil)) - } - - .dayRangeItemProvider(for: dateRanges) { dayRangeLayoutContext in - DayRangeIndicatorView.calendarItemModel( - invariantViewProperties: .init(), - content: .init( - framesOfDaysToHighlight: dayRangeLayoutContext.daysAndFrames.map { $0.frame })) - } - } - - // MARK: Private - - private var selectedDayRange: DayComponentsRange? - private var selectedDayRangeAtStartOfDrag: DayComponentsRange? + return CalendarViewContent( + calendar: calendar, + visibleDateRange: startDate...endDate, + monthsLayout: monthsLayout) + .interMonthSpacing(24) + .verticalDayMargin(8) + .horizontalDayMargin(8) + .dayItemProvider { [weak self, dateRangeColors] day in + guard let self = self else { return nil } + var invariantViewProperties = DayView.InvariantViewProperties.baseInteractive + let date = calendar.date(from: day.components)! + + // Apply colors to date ranges + for (range, color) in dateRangeColors { + if range.contains(date) { + invariantViewProperties.backgroundShapeDrawingConfig.borderColor = color + invariantViewProperties.backgroundShapeDrawingConfig.fillColor = color.withAlphaComponent(0.15) + break + } + } + + return DayView.calendarItemModel( + invariantViewProperties: invariantViewProperties, + content: .init( + dayText: "\(day.day)", + accessibilityLabel: self.dayDateFormatter.string(from: date), + accessibilityHint: nil)) + } + } }