ElegantCalendar 是一个高效且可定制的 SwiftUI 全屏日历。
ElegantCalendar
的灵感来自 TimePage,并且是像这样的优雅演示的更大存储库的一部分:TimePage Clone。它使用了 ElegantPages,这是我专门为分页编写的另一个库,所以请查看它 :)
它主要用于需要使用日历才能正常运行的应用程序(例如 ElegantTimeline),而不是作为全屏日期选择器(演示演示了如果您确实想这样做该怎么做)。
特性
使用 ElegantCalendar
非常简单,只需
import ElegantCalendar
struct ExampleCalendarView: View {
// Start & End date should be configured based on your needs.
let startDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (-30 * 36)))
let endDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (30 * 36)))
@ObservedObject var calendarManager = ElegantCalendarManager(
configuration: CalendarConfiguration(startDate: startDate,
endDate: endDate))
var body: some View {
ElegantCalendarView(calendarManager: calendarManager)
}
}
但是,如果您只想使用单个视图,而不是整个日历视图,您可以这样做
import ElegantCalendar
struct ExampleMonthlyCalendarView: View {
// Start & End date should be configured based on your needs.
let startDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (-30 * 36)))
let endDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (30 * 36)))
@ObservedObject var calendarManager = MonthlyCalendarManager(
configuration: CalendarConfiguration(startDate: startDate,
endDate: endDate))
var body: some View {
MonthlyCalendarView(calendarManager: calendarManager)
}
}
struct ExampleYearlyCalendarView: View {
// Start & End date should be configured based on your needs.
let startDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (-30 * 36)))
let endDate = Date().addingTimeInterval(TimeInterval(60 * 60 * 24 * (30 * 36)))
@ObservedObject var calendarManager = YearlyCalendarManager(
configuration: CalendarConfiguration(startDate: startDate,
endDate: endDate))
var body: some View {
YearlyCalendarView(calendarManager: calendarManager)
}
}
ElegantCalendarView
使用来自 ElegantPages
的 ElegantHPages
视图。 本质上,它只是一个可滑动的 HStack
,可立即加载所有视图。 这也是不建议将 ElegantCalendarView
用作日期选择器的原因。 原因如下。
让我们首先讨论一下月度日历,您可以在其中上下滑动以查看下一个/上个月。此视图使用 ElegantVList
,并且在内存和性能方面非常高效。 对于年度日历,性能同样令人惊叹。 但是,需要注意的是,所有年度视图都必须先加载到内存中并绘制到屏幕上。 这需要几秒钟的时间,具体取决于您的日期范围,范围越宽,时间越长。 但是,一旦此加载过程结束,日历就可以流畅优雅地运行。
那么如何解决这个问题呢? 可以创建一个更简单的年度日历,不需要像当前日历那样多的 CoreGraphics 绘图,或者按需加载年度视图。 第二种方法的问题在于,SwiftUI 在创建视图方面效率低下,因为它在 渲染上花费了大量 CPU。 希望在 SwiftUI 的未来迭代中,渲染会变得更流畅。 至于前一种方法,它似乎是最可行的,如果有足够的人表示有兴趣,我会考虑实施它。 提出一个 issue 让我知道。
public struct CalendarConfiguration: Equatable {
let calendar: Calendar
let ascending: Bool // reverses the order in which the calendar is laid out
let startDate: Date
let endDate: Date
}
public protocol ElegantCalendarDataSource: MonthlyCalendarDataSource, YearlyCalendarDataSource { }
public protocol MonthlyCalendarDataSource {
func calendar(backgroundColorOpacityForDate date: Date) -> Double
func calendar(canSelectDate date: Date) -> Bool
func calendar(viewForSelectedDate date: Date, dimensions size: CGSize) -> AnyView
}
public protocol YearlyCalendarDataSource { }
这允许您自定义任何给定日期的不透明度、是否希望某个日期可点击以及点击日期时显示的附件视图。
public protocol ElegantCalendarDelegate: MonthlyCalendarDelegate, YearlyCalendarDelegate { }
public protocol MonthlyCalendarDelegate {
func calendar(didSelectDay date: Date)
func calendar(willDisplayMonth date: Date)
}
public protocol YearlyCalendarDelegate {
func calendar(didSelectMonth date: Date)
func calendar(willDisplayYear date: Date)
}
这只是为了处理 @Published
包装器的缺点的便捷方法,该包装器不支持 didSet
。 如果您需要在显示月份或日期更改时执行操作,请遵循此约定。
public struct CalendarTheme: Equatable, Hashable {
let primary: Color
}
public extension CalendarTheme {
static let brilliantViolet = CalendarTheme(primary: .brilliantViolet)
static let craftBrown = CalendarTheme(primary: .craftBrown)
static let fluorescentPink = CalendarTheme(primary: .fluorescentPink)
static let kiwiGreen = CalendarTheme(primary: .kiwiGreen)
static let mauvePurple = CalendarTheme(primary: .mauvePurple)
static let orangeYellow = CalendarTheme(primary: .orangeYellow)
static let red = CalendarTheme(primary: .red)
static let royalBlue = CalendarTheme(primary: .royalBlue)
}
ElegantCalendarView(...)
.theme(.mauvePurple)
要配置您自己的主题,只需将您的颜色传递到 CalendarTheme
初始化程序中。 要具有动态外观,请确保您的 Color
同时具有浅色和深色外观。
horizontal
或 vertical
:日历的方向。 默认值为 horizontal
,如 GIF 中所示。 适用于 ElegantCalendarView
& YearlyCalendarView
& MonthlyCalendarView
。ElegantCalendarView(...)
.vertical()
ElegantCalendarView(...)
.allowsHaptics(false)
用户每次点击日期、滚动到新月份或按下滚动返回到今天按钮时都会获得触觉反馈。
MonthlyCalendarView(...)
.frame(width: ...)
可以使用 ElegantCalendarManager
的以下方面
var currentMonth: Date
- 日历视图上显示的当前月份。
var selectedDate: Date?
- 日历视图上选择的日期(如果有)。
var isShowingYearView: Bool
- 是否显示年度视图。 如果为 false,则显示月视图。
func scrollToMonth(_ month: Date, animated: Bool = true)
- 滚动回到某个月份,无论是否具有动画效果。 在此过程中未选择任何日期。
func scrollBackToToday(animated: Bool = true)
- 滚动回到今天,无论是否具有动画效果。 选择今天的日期。
func scrollToDay(_ day: Date, animated: Bool = true)
- 滚动回到某个日期,无论是否具有动画效果。 选择该日期。
GIF 中显示的演示可以在 example repo 上查看。
ElegantCalendar
可通过 Swift Package Manager 获得
使用 Xcode 11,转到 File -> Swift Packages -> Add Package Dependency
并输入 https://github.com/ThasianX/ElegantCalendar
如果您使用 Package.swift
,您还可以轻松地将 ElegantCalendar
添加为依赖项。
let package = Package(
name: "TestProject",
dependencies: [
.package(url: "https://github.com/ThasianX/ElegantCalendar", from: "4.2.0")
],
targets: [
.target(name: "TestProject", dependencies: ["ElegantCalendar"])
]
)
在任何使用 ElegantCalendar
的应用程序或将 ElegantCalendar
用作依赖项的 Swift Package
中
Swift Package Dependencies
部分。在 ElegantCalendar
中,您会看到一个名为 ElegantCalendar.xcassets
的目录。ElegantCalendar.xcassets
拖到目标的 Copy Bundle Resources
中。确保未选中 Copy items if needed
,并且已选中 Create groups
。此步骤至关重要,因为 ElegantCalendar
使用自定义图标,SPM
将在 Swift 5.3 中支持。ElegantCalendar.xcassets
,然后在右侧的检查器中,选择 Identity and Type
。在内部,确保将 Location
设置为 Relative to Build Products
。如果您不知道如何执行此操作,请参阅 Demo
。
如果您发现错误,或者想建议新功能或增强功能,如果您可以首先 搜索 issue 跟踪器,那将是很好的;虽然我们不介意重复项,但保持 issue 的唯一性有助于我们节省时间和整合精力。如果您找不到您的问题,请随时 提交新问题。
此外,这是一个 我发现有用的资源转储 在处理此问题时
本项目根据 MIT 许可证获得许可 - 有关详细信息,请参阅 LICENSE 文件