CleanCocoa/timeline-ui
SwiftUI package to display iOS Calendar.app like timelines with event previews (GitHub mirror of canonical Codeberg repo)
Installation
Add TimelineUI to your project using Swift Package Manager:
dependencies: [
.package(url: "https://codeberg.org/ctietze/timeline-ui.git", from: "1.0.0")
]Note: This project is canonically hosted on Codeberg. GitHub is a mirror.
Quick Start
import TimelineUI
struct ScheduleView: View {
let events: [TimelineItem] = [
TimelineItem(
title: "Team Meeting",
startDate: Date(),
endDate: Date().addingTimeInterval(3600),
color: .blue,
location: "Conference Room A"
),
TimelineItem(
title: "Lunch",
startDate: Date().addingTimeInterval(7200),
endDate: Date().addingTimeInterval(10800),
color: .green
)
]
var body: some View {
DayTimelineView(items: events)
}
}Components
DayTimelineView
Full day timeline that automatically expands to fill available space. Shows hour grid lines with events positioned by time.
DayTimelineView(items: [TimelineItem])CompactTimelineView
Compact timeline window, ideal for widgets or previews.
CompactTimelineView(items: [TimelineItem]) // Fills available height
CompactTimelineView(items: [TimelineItem], heightMode: .flexible) // Same as above
CompactTimelineView(items: [TimelineItem], heightMode: .fixed(hours: 2)) // Fixed 2-hour windowExpandable Timeline
Tap a compact timeline to expand into a full day view with a smooth matched geometry animation:
[Expandable timeline animation]
@State private var isExpanded = false
@Namespace private var timelineNamespace
// Compact view with tap-to-expand
CompactTimelineView(items: items, heightMode: .fixed(hours: 2))
.timelineTransition(in: timelineNamespace)
.onTapGesture {
withAnimation(.spring(duration: 0.4, bounce: 0.15)) {
isExpanded = true
}
}
// Apply expanded overlay at root level
.overlay {
if isExpanded {
ExpandedTimelineContent(items: items) { headerView }
.timelineTransition(in: timelineNamespace)
}
}Access Restricted View
Show a blurred timeline with a permission prompt when calendar access hasn't been granted:
CompactTimelineView(items: [])
.accessRestricted(!hasCalendarAccess) {
AccessPromptView.calendar(style: .compact) {
await requestCalendarAccess()
}
}Customize the prompt text:
AccessPromptView.calendar(
title: "Check for conflicts",
message: "See if this time works with your schedule",
buttonLabel: "Enable Calendar"
) { await requestAccess() }Or use ViewBuilders for full control over icon and button:
AccessPromptView(
title: "Connect Calendar",
message: "Show your events on the timeline",
icon: { Image(systemName: "calendar.badge.plus") },
buttonLabel: { Label("Allow Access", systemImage: "checkmark.circle") }
) { await requestAccess() }Screenshots
| | Light | Dark | |---|:-----:|:----:| | Compact - Focused 2-3 hour window | [Compact light] | [Compact dark] | | Day - Full schedule with hour grid | [Day light] | [Day dark] | | Overlapping - Events arranged side-by-side | [Overlapping light] | [Overlapping dark] | | Many events - Handles busy schedules gracefully | [Many light] | [Many dark] | | Access restricted - Blurred with permission prompt | [Restricted light] | [Restricted dark] |
Requirements
- iOS 26+
- Swift 6.2+
License
MIT
Package Metadata
Repository: CleanCocoa/timeline-ui
Homepage: https://codeberg.org/ctietze/timeline-ui
Stars: 26
Forks: 0
Open issues: 0
Default branch: main
Primary language: swift
License: MIT
Topics: calendar, swift, swiftui, timeline
README: README.md