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

SwiftUI实现新日期到来时自动更新按钮颜色(应用运行时生效)

解决按钮跨天自动更新颜色的问题

你的代码目前存在两个核心问题:

  • 直接读取UserDefaults不会触发视图刷新,即使日期跨天,按钮颜色也不会自动变化
  • 应用运行期间无法感知新一天的到来,无法主动更新视图状态

以下是修改后的完整代码,解决了上述问题:

import SwiftUI
import Combine

struct ButtonView: View {
    @Binding var streak: Int
    // 使用@AppStorage自动同步UserDefaults并触发视图刷新
    @AppStorage("lastUserDate") private var lastUserDate: Date?
    
    // 定时器相关,用于监听新一天的到来
    @State private var dateTimer: Timer.TimerPublisher?
    @State private var cancellable: AnyCancellable?
    
    private var canUserPress: Bool {
        guard let lastDate = lastUserDate else { return true }
        return !Calendar.current.isDateInToday(lastDate)
    }
    
    private var buttonColor: LinearGradient {
        canUserPress ?
        LinearGradient(gradient: Gradient(colors: [.black, .green]), startPoint: .leading, endPoint: .trailing) :
        LinearGradient(gradient: Gradient(colors: [.black, .purple]), startPoint: .leading, endPoint: .trailing)
    }

    var body: some View {
        Button {
            guard canUserPress else { return }
            lastUserDate = Date()
            streak += 1
        } label: {
            ZStack {
                Circle()
                    .fill(buttonColor)
                    .frame(width: 200, height: 200)
                
                Text("Add!")
                    .bold()
                    .font(.title)
                    .foregroundColor(.white)
            }
        }
        .onAppear {
            startDateTimer()
        }
        .onDisappear {
            cancellable?.cancel()
        }
    }
    
    // 设置定时器,在新一天到来时触发视图刷新
    private func startDateTimer() {
        cancellable?.cancel()
        
        // 计算下一次午夜的时间点
        guard let nextMidnight = Calendar.current.nextDate(
            after: Date(),
            matching: DateComponents(hour: 0, minute: 0, second: 0),
            matchingPolicy: .nextTime
        ) else {
            // 计算失败时,每秒检查一次(兜底方案)
            dateTimer = Timer.publish(every: 1, on: .main, in: .common)
            cancellable = dateTimer?.autoconnect().sink { _ in
                objectWillChange.send()
            }
            return
        }
        
        // 设置定时器到午夜触发,触发后重新设置下一天的定时器
        let interval = nextMidnight.timeIntervalSince(Date())
        dateTimer = Timer.publish(every: interval, on: .main, in: .common)
        cancellable = dateTimer?.autoconnect().sink { _ in
            objectWillChange.send()
            startDateTimer()
        }
    }
}

struct ButtonView_Previews: PreviewProvider {
    static var previews: some View {
        ButtonView(streak: .constant(0))
    }
}

关键修改说明:

  1. @AppStorage替代手动UserDefaults操作:
    自动同步lastUserDate和UserDefaults,当值变化时会自动触发视图刷新,无需手动处理数据同步和刷新逻辑。

  2. 添加跨天监听定时器:

    • 计算下一次午夜的时间,设置精准定时器,到点后触发视图刷新,确保新一天到来时按钮颜色自动更新
    • 定时器触发后会重新计算下一次午夜时间,循环监听后续的日期变化
    • 加入兜底方案,避免日期计算失败时无法刷新视图
  3. 简化逻辑:
    移除了冗余的@State lastUserDate,简化canUserPress的判断逻辑,让代码更清晰易维护

内容的提问来源于stack exchange,提问作者Nikita Kolomoec

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 15:45:02