Согласно комментарию в этот вопрос Я сделал пользовательский SwifUI View
на основе TextField
. Он использует цифровую клавиатуру c, вы не можете вводить туда ничего, кроме цифр и точек, может быть только одна точка (точка), и вы можете передать значение Bindable
Double
@State
через View
для ввода. Но есть ошибка: при удалении последнего нуля в «xxx.0» - ноль все равно выходит. Когда вы удаляете точку - ноль становится частью целого числа, поэтому он переходит к «xxx0»
Есть идеи, как это исправить? Я пытался сделать значение целым числом при удалении последнего числа перед точкой - но я не могу уловить момент, когда в строке есть только одна последняя точка.
вот полный код:
import SwiftUI
import Combine
struct DecimalTextField: View {
public let placeHolder: String
@Binding var numericValue: Double
private class DecimalTextFieldViewModel: ObservableObject {
@Published var text = ""{
didSet{
DispatchQueue.main.async {
let substring = self.text.split(separator: Character("."), maxSplits: 2)
switch substring.count{
case 0:
if self.numericValue != 0{
self.numericValue = 0
}
case 1 :
var newValue: Double = 0
if let lastChar = substring[0].last{
if lastChar == Character("."){
newValue = Double(String(substring[0]).dropLast()) ?? 0
}else{
newValue = Double(String(substring[0])) ?? 0
}
}
self.numericValue = newValue
default:
self.numericValue = Double(String("\(String(substring[0])).\(String(substring[1]))")) ?? 0
}
}
}
}
private var subCancellable: AnyCancellable!
private var validCharSet = CharacterSet(charactersIn: "1234567890.")
@Binding private var numericValue: Double{
didSet{
DispatchQueue.main.async {
if String(self.numericValue) != self.text {
self.text = String(self.numericValue)
}
}
}
}
init(numericValue: Binding<Double>, text: String) {
self.text = text
self._numericValue = numericValue
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
init(placeHolder: String = "", numericValue: Binding<Double>){
self._numericValue = numericValue
self.placeHolder = placeHolder
self.viewModel = DecimalTextFieldViewModel(numericValue: self._numericValue, text: numericValue.wrappedValue == Double.zero ? "" : String(numericValue.wrappedValue))
}
var body: some View {
TextField(placeHolder, text: $viewModel.text)
.keyboardType(.decimalPad)
}
}
struct testView: View{
@State var numeric: Double = 0
var body: some View{
return VStack(alignment: .center){
Text("input: \(String(numeric))")
DecimalTextField(placeHolder: "123", numericValue: $numeric)
}
}
}
struct decimalTextField_Previews: PreviewProvider {
static var previews: some View {
testView()
}
}