You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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()
            }
        }
    }
}

问题分析与修正方案

你的现有代码存在几个核心问题:

  1. 直接比较Date()会包含时分秒,同一天不同时间也会被判定为不等,必须只对比日期部分
  2. 按钮内用let TapDate创建局部变量,没有更新@State声明的状态变量
  3. 未处理断签场景,逻辑覆盖不完整

修正后的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 00:50:28