SwiftUI日历isToday函数失效:切换年份后当前日期高亮消失
问题分析与解决方案
问题根源
你的日历切换年份后切回当前年份时高亮消失,核心原因是**ForEach使用day本身作为唯一标识**。当跨年份切换时,同一个日期数字(比如5号)会被SwiftUI视为同一个视图并复用,导致isToday的计算结果没有重新触发,高亮状态未更新。
另外,currentDay、currentMonth、currentYear被定义为private let,只会在视图初始化时获取一次,若App运行超过一天,这些值也不会自动更新,属于潜在问题。
修复步骤
1. 替换ForEach的唯一标识
将每个日期的标识改为年份-月份-日期的组合,确保每个日期的ID全局唯一,避免SwiftUI复用视图:
修改原代码中日期列表的ForEach部分:
// 原代码 ForEach(getDaysInMonth(month: month, year: year), id: \.self) { day in // 修改后 ForEach(getDaysInMonth(month: month, year: year), id: { "\(year)-\(month)-\($0)" }) { day in
2. 优化当前日期的获取方式
将currentDay、currentMonth、currentYear改为计算属性,每次调用时实时获取当前日期,避免初始化后固定不变:
替换原代码中的三个private let:
// 原代码 private let currentDay = Calendar.current.component(.day, from: Date()) private let currentMonth = Calendar.current.component(.month, from: Date()) private let currentYear = Calendar.current.component(.year, from: Date()) // 修改后 private var currentDay: Int { Calendar.current.component(.day, from: Date()) } private var currentMonth: Int { Calendar.current.component(.month, from: Date()) } private var currentYear: Int { Calendar.current.component(.year, from: Date()) }
3. 可选:优化isToday函数(增强健壮性)
直接对比Date对象而非单独的年月日组件,避免因日历设置不同导致的匹配错误:
func isToday(day: Int, month: Int, year: Int) -> Bool { guard let date = Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) else { return false } return Calendar.current.isDateInToday(date) }
完整修复后的核心代码片段
struct MonthOverviewView: View { @State private var year: Int = Calendar.current.component(.year, from: Date()) @State private var scrollProxy: ScrollViewProxy? = nil @State private var hasScrolledToToday = false private let calendar = Calendar.current private let daysOfWeek = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] // 改为计算属性,实时获取当前日期 private var currentDay: Int { Calendar.current.component(.day, from: Date()) } private var currentMonth: Int { Calendar.current.component(.month, from: Date()) } private var currentYear: Int { Calendar.current.component(.year, from: Date()) } var body: some View { VStack { // ... 导航栏代码保持不变 ... ScrollViewReader { proxy in ScrollView { VStack(spacing: 40) { ForEach(1...12, id: \.self) { month in VStack { // ... 月份标题、星期栏代码保持不变 ... LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 7), spacing: 20) { // 修改ForEach的id为唯一日期标识 ForEach(getDaysInMonth(month: month, year: year), id: { "\(year)-\(month)-\($0)" }) { day in if day != 0 { VStack(alignment: .leading) { Text("\(day)") .font(.body) .bold() .foregroundColor(isToday(day: day, month: month, year: year) ? .red : .black) .padding(.bottom, 10) .padding(.leading, 5) Rectangle() .fill(Color.clear) .frame(height: 60) } .frame(minHeight: 120) .border(Color.gray.opacity(0.2), width: 1) .id("day-\(day)-\(month)") } else { Color.clear } } } .padding(.horizontal) } } } .onAppear { // ... onAppear代码保持不变 ... } } } } } // ... getDaysInMonth函数保持不变 ... // 优化后的isToday函数 func isToday(day: Int, month: Int, year: Int) -> Bool { guard let date = Calendar.current.date(from: DateComponents(year: year, month: month, day: day)) else { return false } return Calendar.current.isDateInToday(date) } }
效果验证
修改后,切换到任意年份再切回当前年份,今日的红色高亮会正常显示;即使App运行多天,当前日期的高亮也会自动更新。
内容的提问来源于stack exchange,提问作者Menslo
相关产品推荐
相关产品推荐

