我正在尝试创建一个简单的输入表单,让用户输入四位数的时钟时间 - hh:mm。我想对输入进行可靠的检查,以确保它是有效的时间,并且我想尽早防止错误(例如,您可以输入“21”作为 24 小时制时间的 hh 部分,但不能输入“” 26 英寸)。
我不想为此使用选择器——我发现它们在六十分钟中选择一个很麻烦。
我尝试创建四个文本字段,每个字段分别代表(时钟小时数字 1)、(时钟小时数字 2)、(时钟分钟数字 1)和(时钟分钟数字 2)。这些在下面的代码中显示为 ch1、ch2、cm1 和 cm2。
我在一个单独的线程中添加了数字输入检查,以确保 StackOverflow 上的数字输入干净。每次我获得一个好的数字时,我都想将焦点前进到序列中的下一个文本字段。
我设置了一个散列枚举来标识字段。它们是 f_ch1、f_ch2、f_cm1 和 f_cm2,对应于绑定到不同输入文本字段的数字。
验证工作正常。在验证器的“else”部分中,当我获得有效数字时,我尝试明确地将焦点设置到序列中的下一个字段。焦点确实离开了我当前所在的字段,但下一个字段不会自动获得键盘焦点。除此之外,当在我的 macbook pro 上的模拟器上运行时,调试器窗口报告:
-[RTIInputSystemClient remoteTextInputSessionWithID:performInputOperation:]执行输入操作需要有效的sessionID
我知道这很丑陋,有很多硬编码的基础设施。我很高兴简化,但首先想要真正理解这样的东西应该如何在 SwiftUI 下工作。我很高兴收到任何建议。特别欢迎示例代码;我是一名相当新的 Swift 开发人员,经常被 Apple 文档的简洁性所困扰。
以下是报名表的完整代码:
//
// ContentView.swift
// StackOverflowExample
//
// Created by Mike Olson on 10/8/23.
//
import SwiftUI
import Combine
struct ContentView: View {
// buch of hard-coded infra to automatically advance focus through the form
enum Field: Hashable {
case f_ch1
case f_ch2
case f_cm1
case f_cm2
}
@FocusState private var focusedField: Field?
// Clock digits
@State private var ch1 = ""
@State private var ch2 = ""
@State private var cm1 = ""
@State private var cm2 = ""
var body: some View {
VStack {
HStack {
TextField("h", text: $ch1)
.focusable(true, interactions: .edit)
.focused($focusedField, equals: .f_ch1)
.font(.title3)
.keyboardType(.numberPad)
.onReceive(Just(ch1)) { newValue in
let filtered = newValue.filter { "012".contains($0) } // first digit can be 0, 1, 2
if filtered != newValue {
self.ch1 = filtered
} else {
focusedField = .f_ch2
}
}
TextField("h", text: $ch2)
.focusable(true, interactions: .edit)
.focused($focusedField, equals: .f_ch2)
.font(.title3)
.keyboardType(.numberPad)
.onReceive(Just(ch2)) { newValue in
let filtered = newValue.filter { (self.ch1 == "2" ? "0123" : "0123456789").contains($0) } // if the first digit's a 2, the second can't be bigger than 3
if filtered != newValue {
self.ch2 = filtered
} else {
focusedField = .f_cm1
}
}
Text(":")
.font(.title3)
TextField("m", text: $cm1)
.focusable(true, interactions: .edit)
.focused($focusedField, equals: .f_cm1)
.font(.title3)
.keyboardType(.numberPad)
.onReceive(Just(cm1)) { newValue in
let filtered = newValue.filter { "012345".contains($0) } // first minute digit is 0-5
if filtered != newValue {
self.cm1 = filtered
}
}
TextField("m", text: $cm2)
.focusable(true, interactions: .edit)
.focused($focusedField, equals: .f_cm2)
.font(.title3)
.keyboardType(.numberPad)
.onReceive(Just(cm2)) { newValue in
let filtered = newValue.filter { "0123456789".contains($0) }
if filtered != newValue {
self.cm2 = filtered
}
}
}
.frame(maxWidth: 100)
}
.frame(width: 200, height: 200)
}
}
#Preview {
ContentView()
}
这是做一件简单事情的困难方法。我将在用户完成任何操作之前验证用户输入的完整时间字符串。