Contents

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 window

Expandable 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