最近发现Xcode12的SwiftUI模板变了,适配其他端设备变得更加简单,所以就试着写了一个日历功能,主要参考: www.jianshu.com/p/26c550fc7…
DateInterval 这个时间间隔用的最多,主要是年间隔,月间隔,周间隔。 比如:
///获取当前时间所在年的间隔,即1月1日到12月31日
calendar.dateInterval(of: .year, for: Date())!
LazyVGrid网格视图。用于每月视图布局
///生成网格
LazyVGrid(columns: Array(repeating: GridItem(spacing: 2, alignment: .center), count: 7)) {
///枚举每个月
ForEach(months, id: \.self) { month in
///以每月为一个Section,添加月份
Section(header: Text("1月")) {
///添加日
ForEach(days(for: month), id: \.self) { date in
///在当月就添加
if calendar.isDate(date, equalTo: month, toGranularity: .month) {
Text("1")
}
}
}
}
}
完整的列表滚动式图,这里边我标记了下Section的ID不想要滚动到指定位置的话可以不加
///添加到可以滚动
ScrollView(.vertical, showsIndicators: false){
///添加滚动监听
ScrollViewReader { (proxy: ScrollViewProxy) in
///生成网格
LazyVGrid(columns: Array(repeating: GridItem(spacing: 2, alignment: .center), count: 7)) {
///枚举每个月
ForEach(months, id: \.self) { month in
///以每月为一个Section,添加月份
Section(header: header(for: month)) {
///添加日
ForEach(days(for: month), id: \.self) { date in
///如果不在当月就隐藏
if calendar.isDate(date, equalTo: month, toGranularity: .month) {
content(date).id(date)
} else {
content(date).hidden()
}
}
}
.id(sectionID(for: month))///给每个月创建ID,方便进行滚动标记
}
}
.onAppear(){
///当View展示的时候直接滚动到标记好的月份
proxy.scrollTo(scroolSectionID() )
}
}
}
获取每个月日期的数组,这里就开始用月间隔、周间隔了
///获取每个月,网格范围内的起始结束日期数组
private func days(for month: Date) -> [Date] {
///重点讲解
///先拿到月份间距,例如1号--31号
guard let monthInterval = calendar.dateInterval(of: .month, for: month) else { return [] }
///先获取第一天所在周的周一到周日
let monthFirstWeek = monthInterval.start.getWeekStartAndEnd()
///获取月最后一天所在周的周一到周日
let monthLastWeek = monthInterval.end.getWeekStartAndEnd()
///然后根据月初所在周的周一为0号row 到月末所在周的周日为最后一个row生成数组
return calendar.generateDates(
inside: DateInterval(start: monthFirstWeek.start, end: monthLastWeek.end),
matching: DateComponents(hour: 0, minute: 0, second: 0)
)
}
对Date进行一次扩展方便使用
extension Date {
func getWeekDay() -> Int{
let calendar = Calendar.current
///拿到现在的week数字
let components = calendar.dateComponents([.weekday], from: self)
return components.weekday!
}
///获取当前Date所在周的周一到周日
func getWeekStartAndEnd() -> DateInterval{
var date = self
///因为一周的起始日是周日,周日已经算是下一周了
///如果是周日就到退回去两天
if date.getWeekDay() == 1 {
date = date.addingTimeInterval(-60 * 60 * 24 * 2)
}
///使用处理后的日期拿到这一周的间距: 周日到周六
let week = Calendar.current.dateInterval(of: .weekOfMonth, for: date)!
///处理一下周日加一天到周一
let monday = week.start.addingTimeInterval(60 * 60 * 24)
///周六加一天到周日
let sunday = week.end.addingTimeInterval(60 * 60 * 24)
///生成新的周一到周日的间距
let interval = DateInterval(start: monday, end: sunday)
return interval
}
}
周视图,我们习惯的是周一是开头第一天,周日是最后一天
public struct CalendarWeek: View {
public var body: some View {
HStack{
ForEach(1...7, id: \.self) { count in
Text(Tool.getWeek(week: count))
.frame(maxWidth: .infinity)
}
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}
}
到这个时候基本就能实现了一个符合我们使用习惯的日历,只是没有农历。
写一个静态方法处理一下农历的信息然后就可以了,如果还想添加放假信息,处理一下Day视图就可以了,就注释掉的那段
///获取农历, 节假日名
static func getInfo(date: Date) -> String{
//初始化农历日历
let lunarCalendar = Calendar.init(identifier: .chinese)
///获得农历月
let lunarMonth = DateFormatter()
lunarMonth.locale = Locale(identifier: "zh_CN")
lunarMonth.dateStyle = .medium
lunarMonth.calendar = lunarCalendar
lunarMonth.dateFormat = "MMM"
let month = lunarMonth.string(from: date)
//获得农历日
let lunarDay = DateFormatter()
lunarDay.locale = Locale(identifier: "zh_CN")
lunarDay.dateStyle = .medium
lunarDay.calendar = lunarCalendar
lunarDay.dateFormat = "d"
let day = lunarDay.string(from: date)
//返回农历月
if day == "初一" {
return month
}
//返回农历日期
return day
}
为了方便阅读,我添加了大量的注释。 github.com/jackiehu/Lu…