### UIKit Calendar Setup and Event Handling Source: https://github.com/kvyatkovskys/kvkcalendar/blob/master/README.md Integrate KVKCalendar into a UIKit view controller by subclassing `CalendarView` and implementing the `CalendarDataSource` protocol. This snippet shows how to set up the calendar, load events, and handle system events. ```swift import KVKCalendar final class KVKCalendarVC: UIViewController { var events = [Event]() override func viewDidLoad() { super.viewDidLoad() let calendar = CalendarView() calendar.dataSource = self view.addSubview(calendar) calendar.translatesAutoresizingMaskIntoConstraints = false let top = calendar.topAnchor.constraint(equalTo: view.topAnchor) let leading = calendar.leadingAnchor.constraint(equalTo: view.leadingAnchor) let trailing = calendar.trailingAnchor.constraint(equalTo: view.trailingAnchor) let bottom = calendar.bottomAnchor.constraint(equalTo: view.bottomAnchor) NSLayoutConstraint.activate([top, leading, trailing, bottom]) calendarView.layoutIfNeeded() createEvents { (events) in self.events = events self.calendarView.reloadData() } } override func viewDidLayoutSubviews() { calendarView.layoutIfNeeded() } } extension KVKCalendarVC { func createEvents(completion: ([Event]) -> Void) { let models = // Get events from storage / API let events = models.compactMap({ (item) in var event = Event(ID: item.id) event.start = item.startDate // start date event event.end = item.endDate // end date event event.color = item.color event.isAllDay = item.allDay event.isContainsFile = !item.files.isEmpty event.recurringType = // recurring event type - .everyDay, .everyWeek // Add text event (title, info, location, time) if item.allDay { event.text = "\(item.title)" } else { event.text = "\(startTime) - \(endTime)\n\(item.title)" } return event }) completion(events) } } extension KVKCalendarVC: CalendarDataSource { func eventsForCalendar(systemEvents: [EKEvent]) -> [Event] { // if you want to get events from iOS calendars // set calendar names to style.systemCalendars = ["Test"] let mappedEvents = systemEvents.compactMap { Event(event: $0) } return events + mappedEvents } } ``` ```swift calendar.delegate = self ``` -------------------------------- ### Initialize KVKCalendarView in UIKit Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Initialize KVKCalendarView with custom styles and delegate/dataSource. This example sets up a week view with a twelve-hour time system and integrates system calendars. ```swift import UIKit import KVKCalendar final class CalendarViewController: UIViewController { private lazy var calendarView: KVKCalendarView = { var style = Style() style.defaultType = .week // start on the week view style.startWeekDay = .sunday style.timeSystem = .twelve style.followInSystemTheme = true // automatic dark mode // Show events from iOS system calendars too style.systemCalendars = ["Work", "Personal"] let view = KVKCalendarView(date: Date(), style: style) view.delegate = self view.dataSource = self return view }() override func viewDidLoad() { super.viewDidLoad() view.addSubview(calendarView) // Auto-layout calendarView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ calendarView.topAnchor.constraint(equalTo: view.topAnchor), calendarView.leadingAnchor.constraint(equalTo: view.leadingAnchor), calendarView.trailingAnchor.constraint(equalTo: view.trailingAnchor), calendarView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() calendarView.layoutIfNeeded() // required to trigger internal frame recalculation } } ``` -------------------------------- ### Install KVKCalendar with CocoaPods Source: https://github.com/kvyatkovskys/kvkcalendar/blob/master/README.md Use this command to add KVKCalendar to your project via CocoaPods. Ensure you have CocoaPods installed and have added pods to your Xcode project. ```bash pod 'KVKCalendar' ``` -------------------------------- ### Custom Column Event Layout Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Implement TimelineEventLayout to control event positioning on the timeline. This example divides width among overlapping events. ```swift struct ColumnEventLayout: TimelineEventLayout { func getEventRects( forEvents events: [Event], date: Date?, context: TimelineEventLayoutContext ) -> [CGRect] { let crossEvents = context.calculateCrossEvents(forEvents: events) return events.map { var rect = context.getEventRect( start: event.start, end: event.end, date: date, style: event.style ) // Evenly divide width among overlapping events if let cross = crossEvents[event.start.timeIntervalSince1970] { let count = CGFloat(cross.events.count) let idx = CGFloat(cross.events.firstIndex(where: { $0.ID == event.ID }) ?? 0) rect.size.width = context.pageFrame.width / count - 2 rect.origin.x = context.pageFrame.origin.x + (rect.width + 2) * idx } return rect } } } // Apply via Style: style.timeline.eventLayout = ColumnEventLayout() ``` -------------------------------- ### Implement CalendarDataSource: dequeueCell Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Replace default cells for month, day, week, or list views with custom cells. This example shows how to dequeue and configure custom `MyDayCell` and `MyListCell` instances based on the calendar type. ```swift // Replace a day/week/month/year/list cell with a custom cell func dequeueCell( parameter: CellParameter, type: CalendarType, view: T, indexPath: IndexPath ) -> KVKCalendarCellProtocol? { switch type { case .month, .day, .week: return (view as? UICollectionView)?.kvkDequeueCell(indexPath: indexPath) { (cell: MyDayCell) in cell.configure(date: parameter.date, events: parameter.events) } case .list: return (view as? UITableView)?.kvkDequeueCell { (cell: MyListCell) in cell.configure(date: parameter.date) } default: return nil } } ``` -------------------------------- ### Customize Calendar Style Source: https://github.com/kvyatkovskys/kvkcalendar/blob/master/README.md Define a `Style` struct to customize various aspects of the calendar's appearance, including events, timeline, week, all-day events, header scrolling, month, year, and list views. You can also set locale, calendar, timezone, default calendar type, time hour system, start day of the week, and system calendars. This struct should be added to the `CalendarView`'s `init` method. ```swift public struct Style { public var event = EventStyle() public var timeline = TimelineStyle() public var week = WeekStyle() public var allDay = AllDayStyle() public var headerScroll = HeaderScrollStyle() public var month = MonthStyle() public var year = YearStyle() public var list = ListViewStyle() public var locale = Locale.current public var calendar = Calendar.current public var timezone = TimeZone.current public var defaultType: CalendarType? public var timeHourSystem: TimeHourSystem = .twentyFourHour public var startWeekDay: StartDayType = .monday public var followInSystemTheme: Bool = false public var systemCalendars: Set = [] } ``` -------------------------------- ### Implement CalendarDataSource: willDisplayHeaderSubview Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Inject a custom subview into the scrollable day/week header. This example adds a `UILabel` to the week header displaying the current week number. ```swift // Inject a custom subview into the scrollable day/week header func willDisplayHeaderSubview(date: Date?, frame: CGRect, type: CalendarType) -> UIView? { guard type == .week else { return nil } let label = UILabel(frame: frame) label.text = "Week \(Calendar.current.component(.weekOfYear, from: date ?? Date()))" return label } ``` -------------------------------- ### Deselect Event Programmatically Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Remove the visual selection highlight from an event on the Day or Week timeline. This method is useful for programmatically dismissing an event's selection state, for example, after an action is completed. ```swift func dismissSelectedEvent() { guard let selected = currentSelectedEvent else { return } calendarView.deselectEvent(selected, animated: true) currentSelectedEvent = nil } ``` -------------------------------- ### Build KVKCalendar Event from Scratch Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Create a new Event instance and configure its properties like start/end times, color, and recurrence. Use TextEvent to set display text for different contexts and optionally override default styling with EventStyle. ```swift import KVKCalendar import EventKit // MARK: Build from scratch var event = Event(ID: UUID().uuidString) event.start = Calendar.current.date(bySettingHour: 10, minute: 0, second: 0, of: Date())! event.end = Calendar.current.date(bySettingHour: 11, minute: 30, second: 0, of: Date())! event.color = Event.Color(.systemPurple, alpha: 0.4) event.isAllDay = false event.isContainsFile = true event.recurringType = .everyWeek // .everyDay | .everyWeek | .everyMonth | .everyYear | .none // Set display text per context (timeline / month cell / list cell) event.title = TextEvent( timeline: "10:00 - 11:30\nTeam Stand-up", month: "Team Stand-up 10:00", list: "10:00 - 11:30 Team Stand-up" ) // Per-event style override (optional) var eventStyle = EventStyle() eventStyle.defaultHeight = 40 event.style = eventStyle ``` -------------------------------- ### Global Appearance Configuration with Style Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Configure visual and behavioral aspects of the calendar using the `Style` struct. Mutate sub-styles and pass to the initializer or `updateStyle(_:)`. ```swift @MainActor func makeStyle() -> Style { var style = Style() // Global style.defaultType = .day style.startWeekDay = .monday style.timeSystem = .twentyFour style.isEndOfDayZero = true style.followInSystemTheme = true style.locale = Locale(identifier: "en_US") style.timezone = TimeZone(identifier: "Europe/Berlin")! style.systemCalendars = ["Work"] // Timeline (day/week) style.timeline.startHour = 7 style.timeline.endHour = 22 style.timeline.scrollToHour = 9 style.timeline.isEnabledCreateNewEvent = true style.timeline.createNewEventMethod = .longTap style.timeline.dividerType = .mins15 style.timeline.scale = TimelineStyle.Scale(min: 1, max: 4) style.timeline.eventLayout = DefaultTimelineEventLayout() style.timeline.showLineHourMode = .always style.timeline.scrollLineHourMode = .today style.timeline.currentLineHourStyle = .ios18AndHigher( TimelineStyle.DefaultCurrentLineStyle(lineColor: .systemRed) ) // Month style.month.scrollDirection = .vertical style.month.selectionMode = .multiple style.month.isPagingEnabled = false style.month.isHiddenEventTitle = false style.month.autoSelectionDateWhenScrolling = true // Week style.week.daysInOneWeek = 5 // 5-day work week style.week.viewMode = .default // or .list // All-day events style.allDay.isPinned = true style.allDay.maxHeight = 60 // Event appearance style.event.states = [.move, .resize] style.event.textForNewEvent = "New Event" style.event.iconFile = UIImage(systemName: "paperclip") // Header style.headerScroll.isAnimateTitleDate = true style.headerScroll.titleDateAlignment = .center // Year style.year.isPagingEnabled = true style.year.autoSelectionDateWhenScrolling = true return style } ``` -------------------------------- ### showSkeletonLoading(_:) — Loading State Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Manages the display of skeleton placeholder views to indicate loading states for events. This is applicable to both month and list view types. ```APIDOC ## showSkeletonLoading(_:) — Loading State Show or hide skeleton placeholder views while events are loading. Supported on `.month` and `.list` view types. ```swift func fetchWithSkeleton() { calendarView.showSkeletonLoading(true) Task { let events = await api.fetchEvents() await MainActor.run { self.events = events calendarView.reloadData() calendarView.showSkeletonLoading(false) } } } ``` ``` -------------------------------- ### KVKCalendarView Convenience Initializers Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Demonstrates three convenience initializers for KVKCalendarView, allowing different year range configurations. ```swift // 1. Default — spans 4 years around the given date let calendar1 = KVKCalendarView(frame: .zero, date: Date(), style: myStyle, years: 4) // 2. Explicit start/end year let calendar2 = KVKCalendarView(frame: .zero, date: Date(), style: myStyle, startYear: 2020, endYear: 2030) // 3. Range literal (any ClosedRange or Range conforming to YearRange) let calendar3 = KVKCalendarView(date: Date(), style: myStyle, yearRange: 2022...2026) ``` -------------------------------- ### Implement CalendarDelegate Protocol Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Implement the CalendarDelegate protocol to receive callbacks for user interactions like date selection, event tapping, and event modification. All methods have default no-op implementations. ```swift extension CalendarViewController: CalendarDelegate { // User tapped one or more dates (multi-select supported) func didSelectDates(_ dates: [Date], type: CalendarType, frame: CGRect?) { print("Selected \(dates) in \(type) view") } // User tapped an existing event func didSelectEvent(_ event: Event, type: CalendarType, frame: CGRect?) { presentEventDetail(event) } // User tapped the "+N more" overflow in month view func didSelectMore(_ date: Date, frame: CGRect?) { presentDayView(for: date) } // Event was dragged/resized to new times func didChangeEvent(_ event: Event, start: Date?, end: Date?) { guard let start, let end else { return } updateEvent(id: event.ID, newStart: start, newEnd: end) } // Long-press created a new event placeholder — return modified Event or nil to cancel func willAddNewEvent(_ event: Event, _ date: Date?) -> Event? { var e = event e.title = TextEvent(timeline: "New Event") return e // return nil to prevent creation } // New event was confirmed after willAddNewEvent returned non-nil func didAddNewEvent(_ event: Event, _ date: Date?) { saveEvent(event) } // Currently visible events changed (e.g., scroll to new week) func didDisplayEvents(_ events: [Event], dates: [Date?]) { updateBadge(count: events.count) } // Month scrolled to an upcoming date (before selection is committed) func willSelectDate(_ date: Date, type: CalendarType) { prefetchEvents(around: date) } // Event deselected on timeline func didDeselectEvent(_ event: Event, animated: Bool) { } // The header title date changed func didDisplayHeaderTitle(_ date: Date, style: Style, type: CalendarType) { navigationItem.title = DateFormatter.localizedString(from: date, dateStyle: .medium, timeStyle: .none) } // Preferred size for header or cell func sizeForHeader(_ date: Date?, type: CalendarType) -> CGSize? { type == .month ? CGSize(width: 0, height: 30) : nil } func sizeForCell(_ date: Date?, type: CalendarType) -> CGSize? { type == .month ? CGSize(width: 0, height: 80) : nil } } ``` -------------------------------- ### updateStyle(_:) Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Replace the entire `Style` object at runtime to apply live style updates. This is useful for theme toggles, timezone switches, or layout changes without re-creating the calendar view. ```APIDOC ## updateStyle(_:) — Live Style Updates Replace the entire `Style` at runtime without re-creating the view. Useful for theme toggles, timezone switches, or layout changes. ```swift @objc func toggleTimeFormat() { var updated = calendarView.style updated.timeSystem = calendarView.style.timeSystem == .twentyFour ? .twelve : .twentyFour calendarView.updateStyle(updated) calendarView.reloadData() } @objc func switchTimezone() { var updated = calendarView.style updated.selectedTimeZones = [TimeZone(identifier: "America/New_York")!, TimeZone(identifier: "Europe/London")!] calendarView.updateStyle(updated) } ``` ``` -------------------------------- ### CalendarDelegate Protocol Methods Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt The CalendarDelegate protocol provides callback methods for user interactions with the calendar, such as selecting dates, events, or triggering actions like adding new events. All methods have default no-op implementations. ```APIDOC ## CalendarDelegate Protocol `CalendarDelegate` receives all user interaction callbacks. All methods have default no-op implementations. ```swift extension CalendarViewController: CalendarDelegate { // User tapped one or more dates (multi-select supported) func didSelectDates(_ dates: [Date], type: CalendarType, frame: CGRect?) { print("Selected \(dates) in \(type) view") } // User tapped an existing event func didSelectEvent(_ event: Event, type: CalendarType, frame: CGRect?) { presentEventDetail(event) } // User tapped the "+N more" overflow in month view func didSelectMore(_ date: Date, frame: CGRect?) { presentDayView(for: date) } // Event was dragged/resized to new times func didChangeEvent(_ event: Event, start: Date?, end: Date?) { guard let start, let end else { return } updateEvent(id: event.ID, newStart: start, newEnd: end) } // Long-press created a new event placeholder — return modified Event or nil to cancel func willAddNewEvent(_ event: Event, _ date: Date?) -> Event? { var e = event e.title = TextEvent(timeline: "New Event") return e // return nil to prevent creation } // New event was confirmed after willAddNewEvent returned non-nil func didAddNewEvent(_ event: Event, _ date: Date?) { saveEvent(event) } // Currently visible events changed (e.g., scroll to new week) func didDisplayEvents(_ events: [Event], dates: [Date?]) { updateBadge(count: events.count) } // Month scrolled to an upcoming date (before selection is committed) func willSelectDate(_ date: Date, type: CalendarType) { prefetchEvents(around: date) } // Event deselected on timeline func didDeselectEvent(_ event: Event, animated: Bool) { } // The header title date changed func didDisplayHeaderTitle(_ date: Date, style: Style, type: CalendarType) { navigationItem.title = DateFormatter.localizedString(from: date, dateStyle: .medium, timeStyle: .none) } // Preferred size for header or cell func sizeForHeader(_ date: Date?, type: CalendarType) -> CGSize? { type == .month ? CGSize(width: 0, height: 30) : nil } func sizeForCell(_ date: Date?, type: CalendarType) -> CGSize? { type == .month ? CGSize(width: 0, height: 80) : nil } } ``` ``` -------------------------------- ### Show Skeleton Loading State Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Use this to display or hide skeleton placeholder views during event loading. It's supported on `.month` and `.list` view types. ```swift func fetchWithSkeleton() { calendarView.showSkeletonLoading(true) Task { let events = await api.fetchEvents() await MainActor.run { self.events = events calendarView.reloadData() calendarView.showSkeletonLoading(false) } } } ``` -------------------------------- ### Switch Calendar Module Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Programmatically change the visible calendar module to a different view type (day, week, month, year, list) and optionally scroll to a specific date. Use `animated: true` for a smooth transition. ```swift // Switch to week view, staying on current date calendarView.set(type: .week) // Switch to month view and jump to a specific date let target = Calendar.current.date(byAdding: .month, value: 3, to: Date())! calendarView.set(type: .month, date: target, animated: true) // Available types: .day, .week, .month, .year, .list CalendarType.allCases.forEach { print($0.title) } // → Day, Week, Month, Year, List ``` -------------------------------- ### Style — Global Appearance Configuration Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Configures all visual and behavioral aspects of the calendar using a value type struct. Styles can be mutated and passed to the initializer or updated via `updateStyle(_:)`. ```APIDOC ## Style — Global Appearance Configuration `Style` is a value type (`struct`) that configures every visual and behavioral aspect of the calendar. Mutate sub-styles and pass to the initializer or `updateStyle(_:)`. ```swift @MainActor func makeStyle() -> Style { var style = Style() // Global style.defaultType = .day style.startWeekDay = .monday style.timeSystem = .twentyFour style.isEndOfDayZero = true style.followInSystemTheme = true style.locale = Locale(identifier: "en_US") style.timezone = TimeZone(identifier: "Europe/Berlin")! style.systemCalendars = ["Work"] // Timeline (day/week) style.timeline.startHour = 7 style.timeline.endHour = 22 style.timeline.scrollToHour = 9 style.timeline.isEnabledCreateNewEvent = true style.timeline.createNewEventMethod = .longTap style.timeline.dividerType = .mins15 style.timeline.scale = TimelineStyle.Scale(min: 1, max: 4) style.timeline.eventLayout = DefaultTimelineEventLayout() style.timeline.showLineHourMode = .always style.timeline.scrollLineHourMode = .today style.timeline.currentLineHourStyle = .ios18AndHigher( TimelineStyle.DefaultCurrentLineStyle(lineColor: .systemRed) ) // Month style.month.scrollDirection = .vertical style.month.selectionMode = .multiple style.month.isPagingEnabled = false style.month.isHiddenEventTitle = false style.month.autoSelectionDateWhenScrolling = true // Week style.week.daysInOneWeek = 5 // 5-day work week style.week.viewMode = .default // or .list // All-day events style.allDay.isPinned = true style.allDay.maxHeight = 60 // Event appearance style.event.states = [.move, .resize] style.event.textForNewEvent = "New Event" style.event.iconFile = UIImage(systemName: "paperclip") // Header style.headerScroll.isAnimateTitleDate = true style.headerScroll.titleDateAlignment = .center // Year style.year.isPagingEnabled = true style.year.autoSelectionDateWhenScrolling = true return style } ``` ``` -------------------------------- ### Manual Frame Update for Calendar View Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Recalculate and resize internal sub-views to a new frame. Use this instead of Auto Layout when managing the frame manually. ```swift override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() var frame = view.bounds frame.origin.y = 0 calendarView.reloadFrame(frame) } ``` -------------------------------- ### Implement CalendarDataSource: dequeueAllDayViewEvent Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Provide a custom `UIView` for all-day events. This method allows you to define the appearance and behavior of all-day event views. ```swift // Custom all-day event view func dequeueAllDayViewEvent(_ event: Event, date: Date, frame: CGRect) -> UIView? { let v = MyAllDayView(frame: frame) v.configure(event: event) return v } ``` -------------------------------- ### Implement CalendarDataSource: willDisplayEventOptionMenu Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Provide a context menu for events on iOS 14 and later. This method returns a `UIMenu` containing actions, such as 'Delete', and an optional custom button. ```swift // Context menu for an event (iOS 14+) @available(iOS 14.0, *) func willDisplayEventOptionMenu(_ event: Event, type: CalendarType) -> (menu: UIMenu, customButton: UIButton?)? { let delete = UIAction(title: "Delete", attributes: .destructive) { _ in // handle deletion } return (UIMenu(title: "", children: [delete]), nil) } ``` -------------------------------- ### Implement CalendarDataSource: willDisplayEventView Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Provide a custom `UIView` for a specific event on the timeline by implementing this method. Return a custom `HighlightEventView` if the event's ID matches 'highlight-event', otherwise return nil. ```swift // Supply a fully custom UIView for a specific event on the timeline func willDisplayEventView(_ event: Event, frame: CGRect, date: Date?) -> EventViewGeneral? { guard event.ID == "highlight-event" else { return nil } return HighlightEventView(style: calendarView.style, event: event, frame: frame) } ``` -------------------------------- ### Custom Event View Implementation Source: https://github.com/kvyatkovskys/kvkcalendar/blob/master/README.md Customize the appearance of individual events by creating a subclass of `EventViewGeneral` and returning it from the `willDisplayEventView` delegate method. This allows for unique styling of specific events. ```swift class CustomViewEvent: EventViewGeneral { override init(style: Style, event: Event, frame: CGRect) { super.init(style: style, event: event, frame: frame) } } // an optional function from CalendarDataSource func willDisplayEventView(_ event: Event, frame: CGRect, date: Date?) -> EventViewGeneral? { guard event.ID == id else { return nil } // Replace 'id' with the actual event ID to match return customEventView // Ensure customEventView is an instance of CustomViewEvent } ``` -------------------------------- ### Add KVKCalendar via CocoaPods Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Integrate KVKCalendar into your project using CocoaPods by adding the 'KVKCalendar' pod to your Podfile. ```ruby # Podfile pod 'KVKCalendar' ``` -------------------------------- ### Live Style Updates Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Update the calendar's visual style at runtime by providing a new `Style` object. This is useful for theme toggles, timezone switches, or layout changes without re-creating the view. Remember to call `reloadData()` after updating the style if event display needs to reflect the changes. ```swift @objc func toggleTimeFormat() { var updated = calendarView.style updated.timeSystem = calendarView.style.timeSystem == .twentyFour ? .twelve : .twentyFour calendarView.updateStyle(updated) calendarView.reloadData() } @objc func switchTimezone() { var updated = calendarView.style updated.selectedTimeZones = [TimeZone(identifier: "America/New_York")!, TimeZone(identifier: "Europe/London")!] calendarView.updateStyle(updated) } ``` -------------------------------- ### Implement CalendarDataSource: willDisplayDate Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt This optional method is called just before a date header becomes visible. Use it to perform actions or log information related to the date being displayed and its associated events. ```swift // Called just before a date header becomes visible func willDisplayDate(_ date: Date?, events: [Event]) { print("Displaying date: \(String(describing: date)), event count: \(events.count)") } ``` -------------------------------- ### reloadData() Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Trigger a full data reload from the data source. This method automatically requests EventKit permissions and fetches system calendar events if `style.systemCalendars` is configured. ```APIDOC ## reloadData() — Refresh Event Display Trigger a full data reload from the data source. Automatically requests EventKit permissions and fetches system calendar events when `style.systemCalendars` is non-empty. ```swift // After events array changes self.events = fetchedEvents calendarView.reloadData() // Typical async pattern Task { let fresh = await api.fetchEvents() await MainActor.run { self.events = fresh calendarView.reloadData() } } ``` ``` -------------------------------- ### Implement CalendarDataSource: eventsForCalendar Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Implement the mandatory `eventsForCalendar` method to provide events for the calendar. This method should return an array of KVKCalendar Event objects, combining your app's events with any system events provided. ```swift extension CalendarViewController: CalendarDataSource { // REQUIRED — return events to display; systemEvents contains EKEvents from style.systemCalendars func eventsForCalendar(systemEvents: [EKEvent]) -> [Event] { let mapped = systemEvents.compactMap { Event(event: $0) } return myAppEvents + mapped } ``` -------------------------------- ### SwiftUI Calendar Type Picker Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt A SwiftUI view that presents a menu for switching calendar types. Requires iOS 14.0 or later. ```swift import SwiftUI import KVKCalendar struct CalendarToolbar: View { @State private var selectedType: CalendarType = .week var body: some View { HStack { if #available(iOS 14.0, *) { ItemsMenu( type: $selectedType, items: CalendarType.allCases, showCheckmark: true, color: .red, showDropDownIcon: true ) .onChange(of: selectedType) { newType in // forward to UIKit calendar via coordinator / binding } } Spacer() } .padding() } } ``` -------------------------------- ### set(type:date:animated:) Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Programmatically change the visible calendar module and optionally scroll to a specific date. Supports switching between day, week, month, year, and list views. ```APIDOC ## set(type:date:animated:) — Switch Calendar Module Programmatically change the visible calendar module and optionally scroll to a date. ```swift // Switch to week view, staying on current date calendarView.set(type: .week) // Switch to month view and jump to a specific date let target = Calendar.current.date(byAdding: .month, value: 3, to: Date())! calendarView.set(type: .month, date: target, animated: true) // Available types: .day, .week, .month, .year, .list CalendarType.allCases.forEach { print($0.title) } // → Day, Week, Month, Year, List ``` ``` -------------------------------- ### Programmatic Navigation to Date Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Scroll the visible calendar module to any specified date without reloading event data. Use `animated: true` for a smooth scroll effect. This is useful for navigating to specific dates like today or a past date. ```swift // Jump to today calendarView.scrollTo(Date(), animated: true) // Jump to a specific past date let components = DateComponents(year: 2024, month: 6, day: 15) if let date = Calendar.current.date(from: components) { calendarView.scrollTo(date, animated: false) } ``` -------------------------------- ### activateMovingEventInMonth / movingEventInMonth — Month Drag-and-Drop Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Enables drag-and-drop rescheduling of events within the month view by relaying long-press and pan gesture states. This is intended for use with custom event views. ```APIDOC ## activateMovingEventInMonth / movingEventInMonth — Month Drag-and-Drop Relay long-press and pan gesture states to enable drag-and-drop rescheduling inside the month view when using a custom event view. ```swift // In a UICollectionViewCell or custom EventViewGeneral subclass: @objc func longPressHandler(_ gesture: UILongPressGestureRecognizer) { calendarView.activateMovingEventInMonth( eventView: myEventView, snapshot: myEventView.snapshotView(afterScreenUpdates: true) ?? myEventView, gesture: gesture ) } @objc func panHandler(_ gesture: UIPanGestureRecognizer) { calendarView.movingEventInMonth( eventView: myEventView, snapshot: snapshotView, gesture: gesture ) } ``` ``` -------------------------------- ### reloadFrame(_:) — Manual Frame Update Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Recalculates and resizes all internal sub-views to a new frame. This method should be used instead of Auto Layout when managing the frame manually. ```APIDOC ## reloadFrame(_:) — Manual Frame Update Recalculate and resize all internal sub-views to a new frame. Use this instead of Auto Layout when managing the frame manually. ```swift override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() var frame = view.bounds frame.origin.y = 0 calendarView.reloadFrame(frame) } ``` ``` -------------------------------- ### scrollTo(_:animated:) Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Programmatically scroll the visible calendar module to any specified date without reloading event data. Useful for navigating to specific dates, including past or future dates. ```APIDOC ## scrollTo(_:animated:) — Programmatic Navigation Scroll the visible module to any date without reloading event data. ```swift // Jump to today calendarView.scrollTo(Date(), animated: true) // Jump to a specific past date let components = DateComponents(year: 2024, month: 6, day: 15) if let date = Calendar.current.date(from: components) { calendarView.scrollTo(date, animated: false) } ``` ``` -------------------------------- ### SwiftUI Calendar Integration Source: https://github.com/kvyatkovskys/kvkcalendar/blob/master/README.md Wrap the UIKit `KVKCalendarVC` for use in SwiftUI by implementing the `UIViewControllerRepresentable` protocol. This allows seamless integration of the calendar component within a SwiftUI view hierarchy. ```swift import SwiftUI private struct KVKCalendarWrapper: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> some UIViewController { KVKCalendarVC() } func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {} } struct CalendarContentView: View { var body: some View { NavigationStack { KVKCalendarWrapper() } } } ``` -------------------------------- ### Add KVKCalendar Package to Swift Package Manager Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Add the KVKCalendar package to your Xcode project via Swift Package Manager. Specify the URL and version range. ```swift // Package.swift dependencies: [ .package(url: "https://github.com/kvyatkovskys/KVKCalendar", from: "0.6.30") ], targets: [ .target(name: "MyApp", dependencies: ["KVKCalendar"]) ] ``` -------------------------------- ### Bridge KVKCalendar Event from EKEvent Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Initialize a KVKCalendar Event by bridging from an existing EventKit EKEvent. Provide custom titles for month and list views if needed. ```swift // MARK: Bridge from EKEvent let store = EKEventStore() // … after obtaining EKEvent from store … let bridgedEvent = Event(event: ekEvent, monthTitle: "Stand-up", listTitle: "Stand-up") ``` -------------------------------- ### Month Drag-and-Drop Event Rescheduling Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Relay long-press and pan gesture states to enable drag-and-drop rescheduling within the month view when using a custom event view. ```swift // In a UICollectionViewCell or custom EventViewGeneral subclass: @objc func longPressHandler(_ gesture: UILongPressGestureRecognizer) { calendarView.activateMovingEventInMonth( eventView: myEventView, snapshot: myEventView.snapshotView(afterScreenUpdates: true) ?? myEventView, gesture: gesture ) } ``` ```swift @objc func panHandler(_ gesture: UIPanGestureRecognizer) { calendarView.movingEventInMonth( eventView: myEventView, snapshot: snapshotView, gesture: gesture ) } ``` -------------------------------- ### Custom Date Cell Configuration Source: https://github.com/kvyatkovskys/kvkcalendar/blob/master/README.md Override the `dequeueCell` method in `CalendarDataSource` to provide custom cells for different calendar views (Year, Day, Week, Month, List). This enables detailed customization of how dates are displayed. ```swift func dequeueCell(parameter: CellParameter, type: CalendarType, view: T, indexPath: IndexPath) -> KVKCalendarCellProtocol? where T: UIScrollView { switch type { case .year: let cell = (view as? UICollectionView)?.dequeueCell(indexPath: indexPath) { (cell: CustomYearCell) in // configure the cell } return cell case .day, .week, .month: let cell = (view as? UICollectionView)?.dequeueCell(indexPath: indexPath) { (cell: CustomDayCell) in // configure the cell } return cell case .list: let cell = (view as? UITableView)?.dequeueCell { (cell: CustomListCell) in // configure the cell } return cell } } ``` -------------------------------- ### Implement CalendarDataSource: willDisplaySectionsInListView Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt This method is called when list-view sections are resolved. Use it to inspect or modify the structure of list view sections. ```swift // Called when list-view sections are resolved func willDisplaySectionsInListView(_ sections: [ListViewData.SectionListView]) { print("List sections: \(sections.count)") } } ``` -------------------------------- ### Custom Meeting Event View Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Subclass EventViewGeneral to create a custom view for meeting events, displaying an icon and title. Register this custom view via the CalendarDataSource. ```swift final class MeetingEventView: EventViewGeneral { private let iconView = UIImageView() private let titleLabel = UILabel() override init(style: Style, event: Event, frame: CGRect) { super.init(style: style, event: event, frame: frame) iconView.image = UIImage(systemName: "video") iconView.tintColor = event.textColor iconView.frame = CGRect(x: 4, y: 4, width: 16, height: 16) addSubview(iconView) titleLabel.text = event.title.timeline titleLabel.textColor = event.textColor titleLabel.font = .systemFont(ofSize: 11, weight: .medium) titleLabel.numberOfLines = 2 titleLabel.frame = CGRect(x: 24, y: 2, width: frame.width - 28, height: frame.height - 4) addSubview(titleLabel) } required init?(coder: NSCoder) { fatalError() } } // Register via CalendarDataSource: func willDisplayEventView(_ event: Event, frame: CGRect, date: Date?) -> EventViewGeneral? { guard event.title.timeline.contains("Meeting") else { return nil } return MeetingEventView(style: calendarView.style, event: event, frame: frame) } ``` -------------------------------- ### Custom Cell Context with CellParameter Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt The `CellParameter` struct provides contextual information to the `dequeueCell` method for custom cell rendering. It includes the date, cell type, and events associated with the cell. ```swift // CellParameter fields: // var date: Date? — the date this cell represents // var type: DayType? — .weekend, .weekday, .empty, .selected, etc. // var events: [Event] — events that fall on this date func dequeueCell( parameter: CellParameter, type: CalendarType, view: T, indexPath: IndexPath ) -> KVKCalendarCellProtocol? { guard type == .month, let col = view as? UICollectionView else { return nil } return col.kvkDequeueCell(indexPath: indexPath) { (cell: MonthDayCell) in cell.dateLabel.text = parameter.date.map { "\($0.kvkDay)" } ?? "" cell.dotView.isHidden = parameter.events.isEmpty cell.isWeekend = parameter.type == .weekend } } ``` -------------------------------- ### Refresh Event Display Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Trigger a full data reload for the calendar view. This method automatically requests EventKit permissions and fetches system calendar events if `style.systemCalendars` is configured. Ensure your `events` array is updated before calling `reloadData()`. ```swift // After events array changes self.events = fetchedEvents calendarView.reloadData() // Typical async pattern Task { let fresh = await api.fetchEvents() await MainActor.run { self.events = fresh calendarView.reloadData() } } ``` -------------------------------- ### Configure Recurring Events Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Set the `recurringType` property on an `Event` object to enable automatic projection of recurring events across multiple dates. Supports daily, weekly, monthly, and yearly recurrence. ```swift // Daily recurring event var daily = Event(ID: "daily-standup") daily.start = Calendar.current.date(bySettingHour: 9, minute: 0, second: 0, of: Date())! daily.end = Calendar.current.date(bySettingHour: 9, minute: 30, second: 0, of: Date())! daily.recurringType = .everyDay // Weekly recurring event (same weekday each week) var weekly = Event(ID: "weekly-review") weekly.start = Calendar.current.date(bySettingHour: 14, minute: 0, second: 0, of: Date())! weekly.end = Calendar.current.date(bySettingHour: 15, minute: 0, second: 0, of: Date())! weekly.recurringType = .everyWeek // Monthly recurring var monthly = Event(ID: "monthly-report") monthly.recurringType = .everyMonth // Annual recurring var birthday = Event(ID: "birthday") birthday.recurringType = .everyYear // All available cases: // .everyDay, .everyWeek, .everyMonth, .everyYear, .none ``` -------------------------------- ### CellParameter - Custom Cell Context Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Provides contextual information about a cell being rendered within the calendar. This parameter is passed to the `dequeueCell` method to customize cell appearance based on date, type, and associated events. ```APIDOC ## CellParameter ### Description `CellParameter` is passed to `dequeueCell` to provide contextual information about the cell being rendered. ### Fields - `date` (Date?): The date this cell represents. - `type` (DayType?): The type of day, e.g., `.weekend`, `.weekday`, `.empty`, `.selected`. - `events` ([Event]): A list of events that fall on this date. ### `dequeueCell` Method Signature ```swift func dequeueCell( parameter: CellParameter, type: CalendarType, view: T, indexPath: IndexPath ) -> KVKCalendarCellProtocol? ``` ### Example Usage ```swift // Inside a custom cell dequeuing logic: func dequeueCell( parameter: CellParameter, type: CalendarType, view: T, indexPath: IndexPath ) -> KVKCalendarCellProtocol? { guard type == .month, let col = view as? UICollectionView else { return nil } return col.kvkDequeueCell(indexPath: indexPath) { (cell: MonthDayCell) in cell.dateLabel.text = parameter.date.map { "\($0.kvkDay)" } ?? "" cell.dotView.isHidden = parameter.events.isEmpty cell.isWeekend = parameter.type == .weekend } } ``` ``` -------------------------------- ### kvkOnRotate - Device Rotation Handler Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt A SwiftUI view modifier that triggers an action when the device orientation changes. This is useful for recalculating the layout of calendar views embedded in SwiftUI hierarchies, such as calling `calendarView.layoutIfNeeded()`. ```APIDOC ## kvkOnRotate ### Description A SwiftUI view modifier that fires an action on device orientation changes, useful for triggering `calendarView.layoutIfNeeded()` when the calendar is embedded in a SwiftUI hierarchy. ### Usage ```swift struct CalendarContainer: View { @State private var orientation: UIInterfaceOrientation = .unknown var body: some View { KVKCalendarWrapper() .kvkOnRotate { newOrientation in orientation = newOrientation // trigger layout recalculation if needed } } } ``` ``` -------------------------------- ### Handle Device Rotation in SwiftUI Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Use the `kvkOnRotate` modifier to trigger actions when the device orientation changes within a SwiftUI view. This is useful for recalculating layout, such as for `calendarView.layoutIfNeeded()`. ```swift struct CalendarContainer: View { @State private var orientation: UIInterfaceOrientation = .unknown var body: some { KVKCalendarWrapper() .kvkOnRotate { newOrientation in orientation = newOrientation // trigger layout recalculation if needed } } } ``` -------------------------------- ### SwiftUI Integration of KVKCalendarView Source: https://context7.com/kvyatkovskys/kvkcalendar/llms.txt Wraps the UIKit KVKCalendarView in a UIViewControllerRepresentable for use within a SwiftUI view hierarchy. Handles navigation bar titles and safe area insets differently for iOS 16.0+ and earlier versions. ```swift import SwiftUI import KVKCalendar private struct KVKCalendarWrapper: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> some UIViewController { CalendarViewController() // your UIViewController subclass } func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {} } struct ContentView: View { var body: some View { if #available(iOS 16.0, *) { NavigationStack { KVKCalendarWrapper() .ignoresSafeArea(.container, edges: .bottom) .navigationBarTitleDisplayMode(.inline) .navigationTitle("Calendar") } } else { NavigationView { KVKCalendarWrapper() .edgesIgnoringSafeArea(.bottom) } .navigationViewStyle(.stack) } } } ```