基于Swift和SwiftUI的连续打卡应用:日期计数逻辑实现求助
连续打卡应用的SwiftUI实现问题
我计划用Swift和SwiftUI开发一款连续打卡应用,目标是复刻Duolingo的连续打卡功能。目前在通过Date()实现打卡计数器逻辑时遇到了困难,希望得到相关建议。以下是我已编写的代码:
Date扩展代码
extension Date { // 明日日期 static var tomorrow: Date { return Date().dayAfter } static var today: Date {return Date()} var dayAfter: Date { return Calendar.current.date(byAdding: .day, value: 1, to: Date())! } }
视图结构代码
struct StreakTrial: View { @State var counter = 0 @State var TapDate: Date = Date.today var body: some View { NavigationView { VStack{ Button { // if TapDate != Date.today { // counter += 1 // let TapDate = Date.tomorrow // } // else if TapDate == Date.tomorrow { // counter = counter // } } label: { Image(systemName: "flame") .resizable() .frame(width: 40, height: 50) .padding() .scaledToFit() .background(Color.gray) .foregroundColor(Color.orange) .cornerRadius(12) Text("\(counter)").foregroundColor(.gray) } Text("\(Date())").padding() Text("\(Date().dayAfter)").padding() } } } }
问题分析与修正方案
你的现有代码存在几个核心问题:
- 直接比较
Date()会包含时分秒,同一天不同时间也会被判定为不等,必须只对比日期部分 - 按钮内用
let TapDate创建局部变量,没有更新@State声明的状态变量 - 未处理断签场景,逻辑覆盖不完整
修正后的Date扩展(仅处理日期部分)
extension Date { // 获取当天起始时间(忽略时分秒) var startOfDay: Date { Calendar.current.startOfDay(for: self) } static var today: Date { Date().startOfDay } static var tomorrow: Date { Calendar.current.date(byAdding: .day, value: 1, to: Date.today)! } // 判断是否为同一天 func isSameDay(as otherDate: Date) -> Bool { Calendar.current.isDate(self, inSameDayAs: otherDate) } // 判断是否为目标日期的前一天 func isDayBefore(_ targetDate: Date) -> Bool { guard let dayBeforeTarget = Calendar.current.date(byAdding: .day, value: -1, to: targetDate.startOfDay) else { return false } return self.startOfDay == dayBeforeTarget } }
修正后的视图逻辑
struct StreakTrial: View { @State private var streakCount = 0 @State private var lastCheckInDate: Date = .today var body: some View { NavigationView { VStack(spacing: 20) { Button(action: handleCheckIn) { Image(systemName: "flame") .resizable() .frame(width: 40, height: 50) .padding() .scaledToFit() .background(Color.gray) .foregroundColor(.orange) .cornerRadius(12) Text("\(streakCount)").foregroundColor(.gray) } Text("今日状态: \(hasCheckedInToday ? "已签到" : "未签到")") .foregroundColor(.gray) } .navigationTitle("连续打卡") } } // 检查是否已签到今日 private var hasCheckedInToday: Bool { lastCheckInDate.isSameDay(as: .today) } private func handleCheckIn() { guard !hasCheckedInToday else { // 今日已签到,直接返回 return } let today = Date.today if lastCheckInDate.isDayBefore(today) { // 连续签到,计数+1 streakCount += 1 } else { // 断签后重新签到,重置计数为1 streakCount = 1 } // 更新最后签到日期 lastCheckInDate = today } }
关键改进点
- 所有日期比较基于
startOfDay,彻底避免时分秒干扰 - 新增日期判断方法,让逻辑更直观
- 覆盖三种核心场景:当日重复签到、连续次日签到、断签后重新签到
- 修复状态变量更新逻辑,确保@State变量被正确修改
内容的提问来源于stack exchange,提问作者Prashant Chauhan
相关产品推荐
相关产品推荐

