Lup*_*ati 2 swiftui swiftui-form
我是swiftUI和iO的新手,我试图创建一个仅接受数字的输入字段
TextField("Total number of people", text: $numOfPeople)
Run Code Online (Sandbox Code Playgroud)
TextField还允许使用字母字符,如何限制用户仅输入数字?
And*_*rew 42
一种方法是您可以设置键盘类型,TextField这将限制人们可以输入的内容。
TextField("Total number of people", text: $numOfPeople)
.keyboardType(.numberPad)
Run Code Online (Sandbox Code Playgroud)
苹果的文档,可以发现在这里,你可以看到所有支持的键盘类型的列表在这里。
但是,这种方法只是第一步,并不是唯一的解决方案:
您应该清理输入的数据并确保它是纯数字的。
对于执行该检查的解决方案,请查看下面的John M解决方案。他在解释如何清理数据及其工作原理方面做得很好。
Ber*_*rik 24
可以将 NumberFormatter 交给 TextField 并让它为您处理转换:
struct MyView: View {
@State private var value = 42 // Note, integer value
var body: some View {
// NumberFormatter will parse the text and cast to integer
TextField("title", value: $value, formatter: NumberFormatter())
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,一旦用户完成编辑,就会应用格式化程序。如果用户输入了 NumberFormatter 无法格式化的文本,则该值不会更改。因此,这可能会也可能不会涵盖您的问题“仅接受数字的文本字段”。
hst*_*tdt 12
ViewModifier@John M.'s answer的版本。
import Combine
import SwiftUI
public struct NumberOnlyViewModifier: ViewModifier {
@Binding var text: String
public init(text: Binding<String>) {
self._text = text
}
public func body(content: Content) -> some View {
content
.keyboardType(.numberPad)
.onReceive(Just(text)) { newValue in
let filtered = newValue.filter { "0123456789".contains($0) }
if filtered != newValue {
self.text = filtered
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
len*_*nny 11
在我看来,使用自定义绑定并直接将任何字符串转换为数值要容易得多。通过这种方式,您还可以将 State 变量作为数字而不是string,这是一个巨大的 IMO。
以下是所有需要的代码。请注意,如果字符串无法转换(在这种情况下为零),则使用默认值。
@State private var myValue: Int
// ...
TextField("number", text: Binding(
get: { String(myValue) },
set: { myValue = Int($0) ?? 0 }
))
Run Code Online (Sandbox Code Playgroud)
小智 9
对我来说,在 Xcode 12 和 iOS 14 上,我注意到键入字母确实显示在 中TextField,尽管我不希望它们显示。我希望忽略字母,只允许使用数字。
这是我所做的:
@State private var goalValue = ""
var body: some View {
TextField("12345", text: self.$goalValue)
.keyboardType(.numberPad)
.onReceive(Just(self.goalValue), perform: self.numericValidator)
}
func numericValidator(newValue: String) {
if newValue.range(of: "^\\d+$", options: .regularExpression) != nil {
self.goalValue = newValue
} else if !self.goalValue.isEmpty {
self.goalValue = String(newValue.prefix(self.goalValue.count - 1))
}
}
Run Code Online (Sandbox Code Playgroud)
这里的关键是else if; 这将基础变量的值设置为除最新字符之外的所有内容。
同样值得注意的是,如果您想允许十进制数而不是仅限于整数,您可以将正则表达式字符串更改为"^[\d]+\.?[\d]+$",您必须将其转义为"^[\\d]+\\.?[\\d]+$".
另一种方法可能是创建一个包含 TextField 视图的视图,并保存两个值:一个保存输入字符串的私有变量,以及一个保存 Double 等效项的可绑定值。每次用户键入一个字符时,它都会尝试更新 Double。
这是一个基本的实现:
struct NumberEntryField : View {
@State private var enteredValue : String = ""
@Binding var value : Double
var body: some View {
return TextField("", text: $enteredValue)
.onReceive(Just(enteredValue)) { typedValue in
if let newValue = Double(typedValue) {
self.value = newValue
}
}.onAppear(perform:{self.enteredValue = "\(self.value)"})
}
}
Run Code Online (Sandbox Code Playgroud)
你可以这样使用它:
struct MyView : View {
@State var doubleValue : Double = 1.56
var body: some View {
return HStack {
Text("Numeric field:")
NumberEntryField(value: self.$doubleValue)
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个简单的示例 - 您可能想要添加功能来显示输入不良的警告,也许还需要边界检查等......
您还可以使用简单的格式化程序:
struct AView: View {
@State var numberValue:Float
var body: some View {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return TextField("number", value: $numberValue, formatter: NumberFormatter())
}
Run Code Online (Sandbox Code Playgroud)
用户仍然可以尝试输入一些文本,如下所示:
但格式化程序强制使用一个数字。
尽管显示数字键盘是很好的第一步,但实际上并不能阻止输入错误的数据:
您真正想要做的是清除输入,如下所示:
import SwiftUI
import Combine
struct StackOverflowTests: View {
@State private var numOfPeople = "0"
var body: some View {
TextField("Total number of people", text: $numOfPeople)
.keyboardType(.numberPad)
.onReceive(Just(numOfPeople)) { newValue in
let filtered = newValue.filter { "0123456789".contains($0) }
if filtered != newValue {
self.numOfPeople = filtered
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
每当numOfPeople更改时,都会过滤掉非数字值,并比较过滤后的值以查看是否numOfPeople应该第二次更新,从而用过滤后的输入覆盖错误的输入。
请注意,Just发布者要求您import Combine。
大多数答案都有一些明显的缺点。菲利普的回答是迄今为止最好的恕我直言。大多数其他答案在输入时不会过滤掉非数字字符。相反,您必须等到用户完成编辑后,然后他们更新文本以删除非数字字符。然后下一个常见问题是当输入语言不使用 ASCII 0-9 字符作为数字时,它们不处理数字。
我想出了一个类似于菲利普的解决方案,但它更适合生产。NumericText SPM 包
首先,您需要一种从字符串中正确过滤非数字字符的方法,该方法适用于 unicode。
public extension String {
func numericValue(allowDecimalSeparator: Bool) -> String {
var hasFoundDecimal = false
return self.filter {
if $0.isWholeNumber {
return true
} else if allowDecimalSeparator && String($0) == (Locale.current.decimalSeparator ?? ".") {
defer { hasFoundDecimal = true }
return !hasFoundDecimal
}
return false
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后将文本字段包装在新视图中。我希望我可以作为修饰符来完成这一切。虽然我可以将字符串过滤为一个,但您失去了文本字段绑定数字值的能力。
public struct NumericTextField: View {
@Binding private var number: NSNumber?
@State private var string: String
private let isDecimalAllowed: Bool
private let formatter: NumberFormatter = NumberFormatter()
private let title: LocalizedStringKey
private let onEditingChanged: (Bool) -> Void
private let onCommit: () -> Void
public init(_ titleKey: LocalizedStringKey, number: Binding<NSNumber?>, isDecimalAllowed: Bool, onEditingChanged: @escaping (Bool) -> Void = { _ in }, onCommit: @escaping () -> Void = {}) {
formatter.numberStyle = .decimal
_number = number
if let number = number.wrappedValue, let string = formatter.string(from: number) {
_string = State(initialValue: string)
} else {
_string = State(initialValue: "")
}
self.isDecimalAllowed = isDecimalAllowed
title = titleKey
self.onEditingChanged = onEditingChanged
self.onCommit = onCommit
}
public var body: some View {
return TextField(title, text: $string, onEditingChanged: onEditingChanged, onCommit: onCommit)
.onChange(of: string, perform: numberChanged(newValue:))
.modifier(KeyboardModifier(isDecimalAllowed: isDecimalAllowed))
}
private func numberChanged(newValue: String) {
let numeric = newValue.numericValue(allowDecimalSeparator: isDecimalAllowed)
if newValue != numeric {
string = numeric
}
number = formatter.number(from: string)
}
}
Run Code Online (Sandbox Code Playgroud)
你并不严格需要这个修饰符,但你似乎总是想要它。
private struct KeyboardModifier: ViewModifier {
let isDecimalAllowed: Bool
func body(content: Content) -> some View {
#if os(iOS)
return content
.keyboardType(isDecimalAllowed ? .decimalPad : .numberPad)
#else
return content
#endif
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
第一次发帖,如有错误还请多多包涵。我在当前的项目中一直在努力解决这个问题。许多答案都很好,但仅适用于特定问题,就我而言,没有一个答案满足所有要求。
具体来说,我需要:
Text。John M 的解决方案很棒,但它绑定到一个字符串形式的 @State 私有变量。
jamone 的答案和他的 NumericText 解决方案在很多方面都非常棒,我在项目的 iOS14 版本中实现了它们。不幸的是,它不允许输入负数。
我想出的解决方案主要基于 John M 的答案,但结合了我从 jamone 的 NumericText 代码中学到的 onEditingChanged 的使用。这允许我根据 John M 的解决方案清理用户输入文本,但随后(通过 onEditingChanged 调用的闭包)将该字符串绑定到 Observable Object Double。
因此,我下面的内容确实没有什么新内容,对于更有经验的开发人员来说,这可能是显而易见的。但在我所有的搜索中,我从未偶然发现过这个解决方案,所以我将其发布在这里,以防它对其他人有帮助。
import Foundation
import Combine
class YourData: ObservableObject {
@Published var number = 0
}
func convertString(string: String) -> Double {
guard let doubleString = Double(string) else { return 0 }
return doubleString
}
struct ContentView: View {
@State private var input = ""
@EnvironmentObject var data: YourData
var body: some View {
TextField("Enter string", text: $input, onEditingChanged: {
_ in self.data.number = convertString(string: self.input) })
.keyboardType(.numbersAndPunctuation)
.onReceive(Just(input)) { cleanNum in
let filtered = cleanNum.filter {"0123456789.-".contains($0)}
if filtered != cleanNum {
self.input = filtered
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
147 次 |
| 最近记录: |