@jonasmw:
I am able to reject invalid strings - which can be created by adding an invalid character from the keyboard or pasting in, in my function entryChecker.check(entry)
In my code I have this:
TextField("0.0", text: $entry)
.focused($entryFocused)
.onChange(of: entry, perform: checkEntry)
func checkEntry(value: String) { entryChecker.check(entry: $entry) }
private var entryChecker = FloatEntryValidator(maxlen: 10)
class FloatEntryValidator {
init(maxlen: Int? = nil) { self.maxlen = maxlen}
private let maxlen: Int?
private var prevValidEntry = ""
func check(entry: Binding<String>) {
let value = entry.wrappedValue
if value.isValidFloatEntry(maxlen: maxlen) {
prevValidEntry = value
} else {
entry.wrappedValue = prevValidEntry // Reset
}
}// check
}// FloatEntryValidator
public let minusSign: Character = NumberFormatter().minusSign?.first ?? "-"
public let decimalSep: Character = NSLocale.current.decimalSeparator?.first ?? "."
extension String {
func isValidFloatEntry(maxlen: Int? = nil) -> Bool { // Values that a TextEdit can be allowed to have as the user enters a floating point number
if let maxlen = maxlen, count > maxlen {
return false
}
var parser = StringParser(self)
parser.skipOptional(char: minusSign)
parser.skipDecimals()
parser.skipOptional(char: decimalSep)
parser.skipDecimals()
return parser.finished
}// isValidFloatEntry
}
struct StringParser { // For verifying that a string conforms to specific pattern of sequence. It is valid if finished is true at the end
public init(_ s: String) { string = s; index = s.startIndex }
public let string: String
private var index: String.Index
public var finished: Bool { return index == string.endIndex }
public mutating func skipOptional(char: Character) { // Skip single character that is permitted at the
if !finished && string[index] == char {
index = string.index(after: index)
}
}// skipOptional(Character)
public mutating func skipDecimals() { // Advances index to first place that is not a decimal digit
while !finished {
let uss = string[index].unicodeScalars
if uss.count > 1 || !CharacterSet.decimalDigits.contains(uss.first!) {
return
}
index = string.index(after: index)
}
}// skipDecimal
}// StringParser