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

SwiftUI自定义倒计时DatePicker首次启动默认1分钟问题咨询

问题根源

UIDatePicker的.countDownTimer模式存在系统默认行为:控件初始化完成后如果没有在makeUIView阶段显式设置初始countDownDuration值,第一次用户滚动选择器修改时长时,.valueChanged事件不会触发,也就不会同步修改绑定的duration值,此时duration还是初始的60.0,所以首次启动会按1分钟倒计时。第二次调整时长时控件初始化流程已完成,事件可以正常触发,值同步也就生效了。

修复步骤

只需要在DurationPicker的makeUIView方法中,创建UIDatePicker后主动设置初始时长即可,修改后的代码如下:

import SwiftUI

struct DurationPicker: UIViewRepresentable {
    @Binding var duration: Double

    func makeUIView(context: Context) -> UIDatePicker {
        let datePicker = UIDatePicker()
        datePicker.datePickerMode = .countDownTimer
        // 新增:初始化时就设置初始时长,解决首次修改不触发事件的问题
        datePicker.countDownDuration = duration
        datePicker.addTarget(context.coordinator, action: #selector(Coordinator.updateDuration), for: .valueChanged)
        return datePicker
    }

    func updateUIView(_ datePicker: UIDatePicker, context: Context) {
        // 新增判断:避免不必要的重复赋值,防止循环触发更新
        if datePicker.countDownDuration != duration {
            datePicker.countDownDuration = duration
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject {
        let parent: DurationPicker

        init(_ parent: DurationPicker) {
            self.parent = parent
        }

        @objc func updateDuration(datePicker: UIDatePicker) {
            parent.duration = datePicker.countDownDuration
        }
    }
}

struct ContentView: View {
    
    @State var duration = 60.0
    @State var timeRemaining = 60.0
    @State private var isActive = false
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    
    var body: some View {
        VStack {
            Text("\(timeRemaining)")
                .bold()
                .font(.largeTitle)
            DurationPicker(duration: $duration)
            HStack {
                Button("Cancel") {
                    isActive = false
                }
                .padding()
                Button("Start") {
                    self.timeRemaining = duration
                    isActive = true
                }
                .padding()
            }
        }
        .onReceive(timer) { time in
            guard self.isActive else { return }
            
            if self.timeRemaining > 0 {
                self.timeRemaining -= 1
            }
        }
        .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
            self.isActive = false
        }
        .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
            self.isActive = true
        }

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

额外注意事项

  • UIDatePicker的countDownDuration要求值必须是60的整数倍,系统会自动对非60倍数的数值做取整处理,建议给duration赋值时保证符合该规则,避免出现UI和绑定值不一致的问题。
  • 原代码中的timer属性建议改为let,因为不需要修改它的实例,符合Swift语法规范。

内容的提问来源于stack exchange,提问作者Sebastian Banks

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 10:54:04