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

SwiftUI中如何实现每日零点自动重新启用习惯追踪按钮?

解决方案:基于日期持久化实现按钮每日自动启用

核心思路是通过持久化记录最后完成日期,而非单纯存储按钮禁用状态,结合日期判断自动控制按钮可用性,同时监听App状态变化确保UI实时更新。

步骤1:添加日期工具扩展

先定义一个日期扩展,用于判断两个日期是否为同一天:

import SwiftUI

extension Date {
    func isSameDay(as date: Date) -> Bool {
        Calendar.current.isDate(self, inSameDayAs: date)
    }
}

步骤2:重构按钮视图

替换原有的@State变量,改用@AppStorage持久化存储最后完成日期,通过计算属性动态判断按钮状态,同时合并重复的Button代码:

struct HabitButton: View {
    // 持久化存储最后完成任务的日期
    @AppStorage("lastCompletionDate") private var lastCompletionDate: Date?
    @AppStorage("streak") private var streak: Int = 0
    
    // 动态计算按钮是否禁用:如果今日已完成则禁用
    private var isButtonDisabled: Bool {
        guard let lastDate = lastCompletionDate else { return false }
        return lastDate.isSameDay(as: Date())
    }
    
    var body: some View {
        Button {
            UNUserNotificationCenter.current().setBadgeCount(0)
            lastCompletionDate = Date()
            updateStreak()
        } label: {
            Text("I have eaten an apple")
                .padding(.vertical, 20)
                .foregroundStyle(.white)
                .fontDesign(.rounded)
                .bold()
                .frame(width: 400)
        }
        .background(BasicColor.tint)
        .clipShape(RoundedRectangle(cornerRadius: 20))
        .disabled(isButtonDisabled)
        // App从后台回到前台时,强制刷新状态
        .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
            objectWillChange.send()
        }
        // 视图出现时检查是否跨天
        .onAppear {
            checkAndResetIfNewDay()
        }
    }
    
    // 习惯连续天数更新逻辑(可根据需求调整)
    private func updateStreak() {
        guard let lastDate = lastCompletionDate else {
            streak = 1
            return
        }
        let calendar = Calendar.current
        guard let yesterday = calendar.date(byAdding: .day, value: -1, to: Date()) else { return }
        
        if lastDate.isSameDay(as: yesterday) {
            streak += 1
        } else if !lastDate.isSameDay(as: Date()) {
            streak = 1
        }
        // 同一天重复点击不改变连续天数
    }
    
    // 检查是否跨天,可扩展通知等逻辑
    private func checkAndResetIfNewDay() {
        guard let lastDate = lastCompletionDate else { return }
        if !lastDate.isSameDay(as: Date()) {
            // 可选:发送每日提醒通知等操作
        }
    }
}

#Preview {
    HabitButton()
}

关键改进说明

  1. 状态持久化:用@AppStorage存储lastCompletionDate,避免App重启后丢失状态。
  2. 动态状态判断:isButtonDisabled作为计算属性,实时对比当前日期与最后完成日期,无需手动修改状态,零点后自动启用按钮。
  3. 减少重复代码:合并两个重复的Button,仅通过.disabled()修饰符控制可用性。
  4. App状态监听:通过willEnterForegroundNotification确保App从后台返回时,状态能实时更新。

内容的提问来源于stack exchange,提问作者Fabio Pohl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 12:04:54