A SwiftUI component library for displaying calendar timeline views in iOS apps. Show daily schedules on an hour grid with automatic layout for overlapping events.
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.
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)
}
}Full day timeline that automatically expands to fill available space. Shows hour grid lines with events positioned by time.
DayTimelineView(items: [TimelineItem])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 windowTap a compact timeline to expand into a full day view with a smooth matched geometry 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)
}
}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() }- iOS 26+
- Swift 6.2+
MIT










