Y—Calendar Picker
一个易于使用且高度可定制的月份日历。

该框架提供了一个月份日历选择器,同时支持 UIKit 和 SwiftUI 变体。

Calendar Picker demo animation

许可

Y—CalendarPicker 采用 Apache 2.0 许可证

文档

文档由源代码注释自动生成,并作为静态网站托管在 GitHub Pages 上:https://yml-org.github.io/ycalendarpicker-ios/

使用方法

CalendarPicker (UIKit)

CalendarPickerUIControl 的一个子类,其 API 类似于 UIDatePicker

CalendarView (SwiftUI)

CalendarView 是一个遵循 SwiftUI View 协议的结构体。

初始化器

CalendarPickerCalendarView 都可以使用相同的五个参数初始化(CalendarPicker 在内部使用 CalendarView

init(
    firstWeekday: Int? = nil, 
    appearance: Appearance = .default, 
    minimumDate: Date? = nil, 
    maximumDate: Date? = nil, 
    startDate: Date? = nil,
    locale: Locale? = nil
)

标准初始化器允许您指定一周的第一天、外观、可选的最小和最大日期、日历的开始日期和区域设置,尽管它为所有这些参数提供了合理的默认值。

CalendarPicker 有一个额外的初始化器

init?(coder: NSCoder)

用于 Interface Builder 或 Storyboards(尽管我们建议您在代码中构建 UI)。

以这种方式创建的日历选择器以默认外观开始,但您可以在运行时通过更新其 appearance 属性对其进行自定义。

自定义

CalendarPickerCalendarView 都有一个 appearance 属性,其类型为 Appearance

Appearance 允许您自定义选择器的外观。您可以完全控制使用的颜色、排版和图像。 默认外观与暗模式兼容,并且符合 WCAG 2.0 AA 颜色对比度标准。

/// Appearance for CalendarPicker that contains typography and color properties
public struct Appearance {
    /// Appearance for days within current month
    public var normalDayAppearance: Day
    /// Appearance for days outside current month
    public var grayedDayAppearance: Day
    /// Appearance for today
    public var todayAppearance: Day
    /// Appearance for selected day
    public var selectedDayAppearance: Day
    /// Appearance for disabled day
    public var disabledDayAppearance: Day
    /// Appearance for booked day
    public var bookedDayAppearance: Day
    /// Foreground color and typography for weekdays
    public var weekdayStyle: (textColor: UIColor, typography: Typography)
    /// Image for previous month button
    ///
    /// Images with template rendering mode will be tinted to `monthForegroundColor`.
    public var previousImage: UIImage?
    /// Image for next month button
    ///
    /// Images with template rendering mode will be tinted to `monthForegroundColor`.
    public var nextImage: UIImage?
    /// Foreground color and typography for month (and year)
    public var monthStyle: (textColor: UIColor, typography: Typography)
    /// Background color for calendar view
    public var backgroundColor: UIColor
}

日历有六种不同的外观用于绘制单独的日期

  1. normal:对于当月内未选中的日期
  2. grayed:对于在当月之前或之后未选中的日期(因为我们总是显示 6 行或 42 天)
  3. today:对于未选中时的今天的日期
  4. selected:对于当前选定的日期(如果有)
  5. booked:对于任何已被预订的日期。 这些日期不可选择。
  6. disabled:对于 minimumDate 之前的或 maximumDate 之后的任何日期。 这些日期不可选择。

可以使用 Day 结构自定义每种类型的日期的外观。

/// Appearance for Date
public struct Day {
    /// Typography for day view
    public var typography: Typography
    /// Foreground color for day view
    public var foregroundColor: UIColor
    /// Background color for day view
    public var backgroundColor: UIColor
    /// Border color for day view
    public var borderColor: UIColor
    /// Border width for day view
    public var borderWidth: CGFloat
    /// Hides day view (if true)
    public var isHidden: Bool
}

使用方法 (UIKit)

  1. 如何导入?

    import YCalendarPicker
  2. 创建一个日历选择器

    // Create calendar picker with default values
    let calendarPicker = CalendarPicker()
    
    // add calendar picker to any view
    view.addSubview(calendarPicker)
  3. 自定义然后更新外观

    // Create a calendar picker with the weekday text color set to green
    var calendarPicker = CalendarPicker(
        appearance: CalendarPicker.Appearance(weekdayStyle: (textColor: .green, typography: .weekday)
    )
    
    // Change the weekday text color to red
    calendarPicker.appearance.weekdayStyle.textColor = .red
  4. 更新日历属性

    // set minimum date to yesterday and maximum date to tomorrow
    calendarPicker.minimumDate = Date().previousDate()
    calendarPicker.maximumDate = Date().nextDate()
    
    // select today's date
    calendarPicker.date = Date()
  5. 接收更改通知

    要获得日期更改的通知,只需像使用 UIDatePicker 一样使用 target-action 机制即可。

    // Add target with action
    calendarPicker.addTarget(self, action: #selector(onDateChange), for: .valueChanged)

    如果您想知道用户何时切换月份(通过上一个和下一个按钮),您可以使用选择器的 delegate 属性并遵守 CalendarPickerDelegate 协议。

    // Create calendar picker
    let calendarPicker = CalendarPicker()
    
    // set the delegate to be notified when the month changes
    calendarPicker.delegate = self
    // This will notify when the user presses the next/previous buttons
    extension DemoViewController: CalendarPickerDelegate {
        func calendarPicker(_ calendarPicker: CalendarPicker, didChangeMonthTo date: Date) {
            print("New month: \(date)")
        }
    }

使用方法 (SwiftUI)

我们的日历选择器也支持 Swift UI!

  1. 如何导入?

    import YCalendarPicker
  2. 创建一个日历视图 CalendarView 遵循 SwiftUI 的 View 协议,因此我们可以将 CalendarView 直接与任何 SwiftUI 视图集成。

    var body: some View {
        CalendarView()
    }
  3. 自定义然后更新外观

    struct CustomCalendar {
        @State var calendar: CalendarView = {
            // Create a calendar picker with the weekday text color set to green
            var calendar = CalendarView()
            calendar.appearance.weekdayStyle.textColor = .green
            return calendar
        }()
    }
    
    extension CustomCalendar: View {
        public var body: some View {
            VStack {
                calendar
                Button("Go Red") {
                    // Change the weekday text color to red
                    calendar.appearance.weekdayStyle.textColor = .red
                }
            }
        }
    }
  4. 更新日历属性

    struct CustomCalendar {
        @State var calendar = CalendarView()
    }
    
    extension CustomCalendar: View {
        var body: some View {
            VStack {
                calendar
                Button("Set Min/Max") {
                    // set minimum date to yesterday and maximum date to tomorrow
                    calendar.minimumDate = Date().previousDate()
                    calendar.maximumDate = Date().nextDate()
                }
                Button("Select Today") {
                    // select today's date
                    calendar.date = Date()
                }
            }
        }
    }
  5. 接收更改通知 要在用户选择日期或更改月份时收到通知,您可以使用 delegate 属性并遵守 CalendarViewDelegate 协议。

    extension DemoView: CalendarViewDelegate {
        // Date was selected
        func calendarViewDidSelectDate(_ date: Date?) {
            if let date {
                print("Selected: \(date)")
            } else {
                print("Selection cleared")
            }
        }
        
        // Month was changed
        func calendarViewDidChangeMonth(to date: Date) {
            print("New month: \(date)")
        }
    }

依赖项

Y—CalendarPicker 依赖于我们的 Y—CoreUIY—MatterType 框架(两者都是开源的,并采用 Apache 2.0 许可证)。

安装

您可以通过将 Y—CalendarPicker 添加为包依赖项来将其添加到 Xcode 项目。

  1. 文件菜单中,选择添加包...
  2. 在包存储库 URL 文本字段中输入“https://github.com/yml-org/ycalendarpicker-ios
  3. 点击 添加包

为 Y—CalendarPicker 做出贡献

要求

SwiftLint (linter)

brew install swiftlint

Jazzy (文档)

sudo gem install jazzy

设置

克隆 repo 并在 Xcode 中打开 Package.swift

版本控制策略

我们使用 语义版本控制

{major}.{minor}.{patch}

例如。

1.0.5

分支策略

我们为我们的框架使用简化的分支策略。

分支命名约定

feature/{ticket-number}-{short-description}
bugfix/{ticket-number}-{short-description}

例如。

feature/CM-44-button
bugfix/CM-236-textview-color

拉取请求

在提交拉取请求之前,您应该

  1. 编译并确保没有警告和错误。
  2. 运行所有单元测试并确认一切通过。
  3. 检查单元测试覆盖率并确认所有新的/修改的代码都被完全覆盖。
  4. 从命令行运行 swiftlint 并确认没有违规行为。
  5. 从命令行运行 jazzy 并确认您有 100% 的文档覆盖率。
  6. 考虑使用 git rebase -i HEAD~{commit-count} 将最后 {commit-count} 个提交合并成功能块。
  7. 如果父分支 (通常是 main) 的 HEAD 在您创建分支后已更新,请使用 git rebase main 来 rebase 您的分支。
    • 永远不要将父分支合并到您的分支中。
    • 始终根据父分支 rebase 您的分支。

提交拉取请求时

合并拉取请求时

发布新版本

生成文档(通过 Jazzy)

您可以使用终端中的以下命令直接从源代码生成自己的本地文档集

jazzy

这会在 /docs 下生成一组文档。 默认配置在默认配置文件 .jazzy.yaml 文件中设置。

要查看其他文档选项,请输入

jazzy --help

每次提交被推送到 main 时,GitHub Action 会自动运行,运行 Jazzy 以生成我们在 GitHub 页面上的文档:https://yml-org.github.io/ycalendarpicker-ios/