如何创建一个只允许用户输入数字和一个点的 swiftui 文本字段?
换句话说,它在用户输入时逐位检查,如果输入是数字或点,并且文本字段没有另一个点,则该数字被接受,否则数字条目将被忽略。
使用步进器不是一种选择。
最佳答案
SwiftUI 不允许您为 TextField
指定一组允许的字符。实际上,这与 UI 本身无关,而是与您如何管理背后的模型有关。在这种情况下,模型是 TextField
后面的文本。所以,你需要改变你的 View 模型。
如果您在 $
属性上使用 @Published
符号,您可以访问 Publisher
属性本身后面的 @Published
。然后您可以将您自己的订阅者附加到发布者并执行您想要的任何检查。在这种情况下,我使用 sink
函数将基于闭包的订阅者附加到发布者:
/// Attaches a subscriber with closure-based behavior.
///
/// This method creates the subscriber and immediately requests an unlimited number of values, prior to returning the subscriber.
/// - parameter receiveValue: The closure to execute on receipt of a value.
/// - Returns: A cancellable instance; used when you end assignment of the received value. Deallocation of the result will tear down the subscription stream.
public func sink(receiveValue: @escaping ((Self.Output) -> Void)) -> AnyCancellable
实现:
import SwiftUI
import Combine
class ViewModel: ObservableObject {
@Published var text = ""
private var subCancellable: AnyCancellable!
private var validCharSet = CharacterSet(charactersIn: "1234567890.")
init() {
subCancellable = $text.sink { val in
//check if the new string contains any invalid characters
if val.rangeOfCharacter(from: self.validCharSet.inverted) != nil {
//clean the string (do this on the main thread to avoid overlapping with the current ContentView update cycle)
DispatchQueue.main.async {
self.text = String(self.text.unicodeScalars.filter {
self.validCharSet.contains($0)
})
}
}
}
}
deinit {
subCancellable.cancel()
}
}
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
TextField("Type something...", text: $viewModel.text)
}
}
重要的是要注意:
$text
($
属性上的 @Published
符号)为我们提供了一个 Published<String>.Publisher
类型的对象,即发布者 $viewModel.text
($
上的 @ObservableObject
符号)给了我们一个 Binding<String>
那是完全不同的两件事。
编辑 :如果您愿意,您甚至可以使用此行为创建自己的自定义
TextField
。假设您要创建一个 DecimalTextField
View :import SwiftUI
import Combine
struct DecimalTextField: View {
private class DecimalTextFieldViewModel: ObservableObject {
@Published var text = ""
private var subCancellable: AnyCancellable!
private var validCharSet = CharacterSet(charactersIn: "1234567890.")
init() {
subCancellable = $text.sink { val in
//check if the new string contains any invalid characters
if val.rangeOfCharacter(from: self.validCharSet.inverted) != nil {
//clean the string (do this on the main thread to avoid overlapping with the current ContentView update cycle)
DispatchQueue.main.async {
self.text = String(self.text.unicodeScalars.filter {
self.validCharSet.contains($0)
})
}
}
}
}
deinit {
subCancellable.cancel()
}
}
@ObservedObject private var viewModel = DecimalTextFieldViewModel()
var body: some View {
TextField("Type something...", text: $viewModel.text)
}
}
struct ContentView: View {
var body: some View {
DecimalTextField()
}
}
这样您就可以使用您的自定义文本字段,只需编写:
DecimalTextField()
您可以随时随地使用它。
关于ios - 如何创建只接受数字和一个点的 SwiftUI TextField?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57822749/