Mac上的DatePicker在按下返回键之前不会保存日期

Posted

技术标签:

【中文标题】Mac上的DatePicker在按下返回键之前不会保存日期【英文标题】:DatePicker on Mac not saving date until return key is pressed 【发布时间】:2020-08-10 10:18:36 【问题描述】:

我正在使用 Mac Catalyst 将我的 iPad 应用程序调整到 Mac,但 datePicker 出现问题(它的时间为 datePickerMode)。在 iPad 上,datePicker 是一个***,只要用户在日期选择器上滚动,就会触发 dateChanged 操作。但在 Mac 上,日期选择器不是滚动条,而是一种文本输入。我可以在 Mac 上键入和更改所有时间值,但是在按下返回键之前不会触发 dateChanged 操作。

我想在用户输入某个时间时触发 dateChange 操作。我该怎么做?我尝试向 datePicker 添加不同的目标,但没有任何效果。

我实际上更喜欢在 Mac 上使用日期滚动器,所以如果有人知道如何执行此操作,我将不胜感激(我在互联网上到处寻找,但一无所获)!

这是我的代码:

class DateVC: UIViewController 
     @IBOutlet weak var datePicker: UIDatePicker!

     override func viewDidLoad() 
          super.viewDidLoad()

          //Just show the time
          datePicker.datePickerMode = .time
    

     //Action connected to datePicker. This is not called until I press enter on Mac
     @IBAction func datePickerChanged(_ sender: Any) 
        //do actions
     


【问题讨论】:

我没有适用于 Mac 的日期滚动条,但我有一个不错的 SwiftUI 时钟,带有时针和分针,您可以在 ios 和 maccatalyst 中使用。它可能很有趣。库位于:github.com/workingDog/ClockTimePicker 使用示例:github.com/workingDog/ClockPicker 这听起来像是 Mac Catalyst 中的一个错误。我有一个带有 NSDatePicker 的 mac 应用程序,即使用户使用键盘输入日期,该操作也会始终触发。 我遇到了同样的错误并通过 Xcode 提交了错误报告。 Apple 已跟进请求更多信息,因此我制作了一个示例项目供他们运行,因此希望这将很快得到解决。非常令人沮丧。 【参考方案1】:

这是一个 SwiftUI 解决方案,可在 iPad、iPhone 和 Mac Catalyst 上显示日期和时间滚动选择器。无需按返回键即可工作。如果需要,您可以轻松地仅显示 HoursMinutesPicker。

import SwiftUI

struct ContentView: View 
@State var date = Date()
var body: some View 
    NavigationView 
        NavigationLink(destination: DateHMPicker(date: self.$date)) 
            VStack 
                Text("Show time")
                Text("\(self.date)")
            
        
    .navigationViewStyle(StackNavigationViewStyle())



struct DateHMPicker: View 
var titleKey: LocalizedStringKey = ""
@Binding var date: Date

var body: some View 
    HStack 
        Spacer()
        DatePicker(titleKey, selection: self.$date, displayedComponents: .date).datePickerStyle(WheelDatePickerStyle())
        HoursMinutesPicker(date: self.$date)
        Spacer()
    



struct HoursMinutesPicker: View 
@Binding var date: Date
@State var hours: Int = 0
@State var minutes: Int = 0

var body: some View 
    HStack 
        Spacer()
        Picker("", selection: Binding<Int>(
            get:  self.hours,
            set : 
                self.hours = $0
                self.update()
        )) 
            ForEach(0..<24, id: \.self)  i in
                Text("\(i) hours").tag(i)
            
        .pickerStyle(WheelPickerStyle()).frame(width: 90).clipped()
        Picker("", selection: Binding<Int>(
            get:  self.minutes,
            set : 
                self.minutes = $0
                self.update()
        )) 
            ForEach(0..<60, id: \.self)  i in
                Text("\(i) min").tag(i)
            
        .pickerStyle(WheelPickerStyle()).frame(width: 90).clipped()
        Spacer()
    .onAppear(perform: loadData)


func loadData() 
    self.hours = Calendar.current.component(.hour, from: date)
    self.minutes = Calendar.current.component(.minute, from: date)


func update() 
    if let newDate = Calendar.current.date(bySettingHour: self.hours, minute: self.minutes, second: 0, of: date) 
        date = newDate
    


【讨论】:

谢谢,但我不想拥有 WheelPickerStyle。 Mac 催化剂上的 UIDatePicker 的优点是它是一种文本字段格式,因此它允许用户使用键盘输入日期/时间 对不起,我看错了你的问题。我认为“我实际上更喜欢在 Mac 上使用日期滚动条”意味着您想要 WheelPickerStyle。您是否正在寻找仅用于时间的文本字段类型? 对不起,原来的问题不是我的,所以我看到你的困惑。我希望将它用于日期和时间。在 Mac 催化剂中,它为日期的文本字段提供了一个不错的日历【参考方案2】:

如何将 DatePicker 用于日期部分,将以下文本字段用于时间输入部分。

import SwiftUI
import Combine

struct ContentView: View 
@State var date = Date()
var body: some View 
    NavigationView 
        NavigationLink(destination: DateHMPicker(date: self.$date)) 
            VStack 
                Text("Show time")
                Text("\(self.date)")
            
        
    .navigationViewStyle(StackNavigationViewStyle())



struct DateHMPicker: View 
@State var labelText = ""
@Binding var date: Date

var body: some View 
    HStack 
        Spacer()
        DatePicker(labelText, selection: self.$date, displayedComponents: .date)
        Spacer()
        HoursMinutesPicker(date: self.$date).frame(width: 90)
    .fixedSize()



struct TextFieldTime: View 
let range: ClosedRange<Int>
@Binding var value: Int
var handler: () -> Void

@State private var isGood = false
@State private var textValue = ""
@State private var digits = 2

var body: some View 
    TextField("", text: $textValue)
        .font(Font.body.monospacedDigit())
        .onReceive(Just(textValue))  txt in
            // must be numbers
            var newTxt = txt.filter "0123456789".contains($0)
            if newTxt == txt 
                // restrict the digits
                if newTxt.count > self.digits 
                    newTxt = String(newTxt.dropLast())
                
                // check the number
                self.isGood = false
                if let number = NumberFormatter().number(from: newTxt) 
                    if self.range.contains(number.intValue) 
                        self.textValue = newTxt
                        self.value = number.intValue
                        self.isGood = true
                     else 
                        self.textValue = self.textValue.count == 1
                            ? String(self.range.lowerBound) : String(self.textValue.dropLast())
                    
                
                if self.value >= 0 && self.isGood 
                    self.handler()
                
             else 
                self.textValue = newTxt.isEmpty ? String(self.range.lowerBound) : newTxt
            

    .onAppear(perform: 
        self.textValue = String(self.value)
        self.digits = String(self.range.upperBound).count
    )
        .fixedSize()



struct HoursMinutesPicker: View 
@Binding var date: Date
@State var separator = ":"

@State var hours: Int = 0
@State var minutes: Int = 0

var body: some View 
    HStack (spacing: 1) 
        TextFieldTime(range: 0...23, value: self.$hours, handler: self.update)
        Text(separator)
        TextFieldTime(range: 0...59, value: self.$minutes, handler: self.update)
    .onAppear(perform: loadData).padding(5)


func loadData() 
    self.hours = Calendar.current.component(.hour, from: date)
    self.minutes = Calendar.current.component(.minute, from: date)


func update() 
    let baseDate = Calendar.current.dateComponents([.year, .month, .day], from: date)
    var dc = DateComponents()
    dc.year = baseDate.year
    dc.month = baseDate.month
    dc.day = baseDate.day
    dc.hour = self.hours
    dc.minute = self.minutes
    if let newDate = Calendar.current.date(from: dc), date != newDate 
        date = newDate
    


【讨论】:

这个答案是否提供了一种解决方法,不需要用户在调整时间后按回车键?【参考方案3】:

因为您的函数与 @IBAction 相关联,该函数将在操作时调用,例如“按钮按下”

你应该遵循不同的方法。

let datePicker = UIDatePicker()
    datePicker.datePickerMode = .date
    dateTextField.inputView = datePicker



datePicker.addTarget(self, action: #selector(datePickerChanged(picker:)), for: .valueChanged)

这是你的功能:

@objc func datePickerChanged(picker: UIDatePicker) 
    //do your action here

【讨论】:

这不会在 MacCatalyst 上触发而不按回车键【参考方案4】:

我没有找到使用内置 UIDatePicker 的解决方案。我最终转而使用JBCalendarDatePicker,它实现了相同的外观/感觉。

【讨论】:

【参考方案5】:

如果您更喜欢 Mac Catalyst 中 DatePicker 的滚轮格式,请将您的 Date Picker 样式更改为 Wheels。它将在 Mac 上正确显示。

我将我的 iPhone/iPad 应用程序转换为在 Mac Catalyst 上运行。我在 MacOS Catalina 10.15.5 上使用 Xcode 11.4.1。我在应用程序的 Mac 版本上将 DatePicker 显示为***时遇到问题。在 Mac 模拟器上,它显示为文本字段,但单击日期字段之一时会显示日历。我觉得继续使用滚轮显示会带来更好的用户体验。

一个非常简单的解决方法是在 Storyboard 中选择 DatePicker。显示属性检查器。将您的样式从“自动”更改为“***”,在 Mac Catalyst 版本上,显示将返回显示为***。

【讨论】:

我还发现您应该检查属性检查器下的模式设置,以查看日期和时间、日期或时间,以便***显示正确的值。【参考方案6】:

大约 1 周前,我向 Apple 提交了错误报告。 现在我执行以下操作来强制日期选择器使用轮格式。这会在***旋转时触发 onchangedlistener。

if #available(macCatalyst 13.4, *) 
    datePickerView.preferredDatePickerStyle = .wheels

【讨论】:

以上是关于Mac上的DatePicker在按下返回键之前不会保存日期的主要内容,如果未能解决你的问题,请参考以下文章

在按下完成或uitextfield的返回键时调用函数

为啥鼠标右键单击在按下时会触发“pointerdown”事件,但在释放时不会触发“pointerup”事件?

我可以知道在按 Enter 之前按下了哪个键盘键吗

在按下 Arduino 上的特定键盘按钮之前,如何让步进电机运行?

iPhone如何在按下音量键时隐藏音量覆盖

mac重装系统