Swiftui NumberField或如何使TextField输入双精度、浮点或其他带点的数字
根据中的评论,我根据Swiftui NumberField或如何使TextField输入双精度、浮点或其他带点的数字,swiftui,textfield,numeric,Swiftui,Textfield,Numeric,根据中的评论,我根据文本字段制作了一个自定义SwifUI视图。它使用数字键盘,只能输入数字和点,只能输入一个点,您可以通过视图传递一个可绑定的双字符串{ 设四舍五入=双精度-双精度(整数(双精度))==0 var result=“” 如果是double!=double.zero{ 结果=四舍五入?字符串(整数(双精度)):字符串(双精度) } 返回结果 } 结构域:视图{ 公共let占位符:字符串 @绑定变量numericValue:双精度 私有类DecimalTextFieldViewMode
文本字段
制作了一个自定义SwifUI视图
。它使用数字键盘,只能输入数字和点,只能输入一个点,您可以通过视图
传递一个可绑定的双
值进行输入。
但是有一个错误:当你删除“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()
}
}
导入快捷界面
进口联合收割机
结构域:视图{
公共let占位符:字符串
@绑定变量numericValue:双精度
私有类DecimalTextFieldViewModel:ObserveObject{
@已发布的var text=“”{
迪塞特{
DispatchQueue.main.async{
让substring=self.text.split(分隔符:字符(“.”),最大拆分:2)
开关子字符串.count{
案例0:
如果self.numericValue!=0{
self.numericValue=0
}
案例1:
var newValue:Double=0
如果让lastChar=substring[0]。last{
如果lastChar==字符(“.”){
newValue=Double(字符串(子字符串[0]).dropLast())??0
}否则{
newValue=Double(字符串(子字符串[0])??0
}
}
self.numericValue=newValue
违约:
self.numericValue=Double(字符串(\(字符串(子字符串[0]))。\(字符串(子字符串[1])))))?0
}
}
}
}
私有var子可取消:任何可取消!
私有变量validCharSet=CharacterSet(charactersIn:“1234567890.”)
@绑定私有var numericValue:双精度{
迪塞特{
DispatchQueue.main.async{
如果字符串(self.numericValue)!=self.text{
self.text=字符串(self.numericValue)
}
}
}
}
init(numericValue:Binding,text:String){
self.text=文本
self.\u numericValue=numericValue
subCancellable=$text.sink{val in
//检查新字符串是否包含任何无效字符
如果val.rangeOfCharacter(from:self.validCharSet.inversed)!=nil{
//清理字符串(在主线程上执行此操作以避免与当前ContentView更新周期重叠)
DispatchQueue.main.async{
self.text=字符串(self.text.unicodeScalars.filter{
self.validCharSet.contains($0)
})
}
}
}
}
脱硝{
子可取消。取消()
}
}
@观察对象私有var视图模型:DecimalTextFieldViewModel
init(占位符:String=”“,numericValue:Binding){
self.\u numericValue=numericValue
self.placeHolder=占位符
self.viewModel=DecimalTextFieldViewModel(numericValue:self.\u numericValue,text:numericValue.wrappedValue==Double.zero?“:字符串(numericValue.wrappedValue))
}
var body:一些观点{
文本字段(占位符,文本:$viewModel.text)
.键盘类型(.decimalPad)
}
}
结构测试视图:视图{
@状态变量数值:双精度=0
var body:一些观点{
返回VStack(对齐:。中心){
文本(“输入:\(字符串(数字))”)
DecimalTextField(占位符:“123”,数值:$numeric)
}
}
}
结构decimalTextField\u预览:PreviewProvider{
静态var预览:一些视图{
testView()
}
}
我不确定我是否真的把每件事都做好了,但看起来我解决了这个问题。
代码如下:
import SwiftUI
import Combine
fileprivate func getTextOn(double: Double) -> String{
let rounded = double - Double(Int(double)) == 0
var result = ""
if double != Double.zero{
result = rounded ? String(Int(double)) : String(double)
}
return result
}
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)
if substring.count == 0{
if self.numericValue != 0{
self.numericValue = 0
}
}else if substring.count == 1{
var newValue: Double = 0
if let lastChar = substring[0].last{
let ch = String(lastChar)
if ch == "."{
newValue = Double(String(substring[0]).dropLast()) ?? 0
}else{
newValue = Double(String(substring[0])) ?? 0
}
}
if self.numericValue != newValue{
self.numericValue = newValue
}
}else{
let newValue = Double(String("\(String(substring[0])).\(String(substring[1]))")) ?? 0
if self.numericValue != newValue{
self.numericValue = newValue
}
}
}
}
}
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: getTextOn(double: 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("123", numericValue: $numeric)
}
}
}
struct decimalTextField_Previews: PreviewProvider {
static var previews: some View {
testView()
}
}
导入快捷界面
进口联合收割机
fileprivate func getTextOn(double:double)->字符串{
设四舍五入=双精度-双精度(整数(双精度))==0
var result=“”
如果是double!=double.zero{
结果=四舍五入?字符串(整数(双精度)):字符串(双精度)
}
返回结果
}
结构域:视图{
公共let占位符:字符串
@绑定变量numericValue:双精度
私有类DecimalTextFieldViewModel:ObserveObject{
@已发布的var text=“”{
迪塞特{
DispatchQueue.main.async{
让substring=self.text.split(分隔符:字符(“.”),最大拆分:2)
如果substring.count==0{
如果self.numericValue!=0{
self.numericValue=0
}
}如果substring.count==1,则为else{
var newValue:Double=0
如果让lastChar=substring[0]。last{
设ch=String(lastChar)
如果ch==”{
newValue=Double(字符串(子字符串[0]).dropLast())??0
}否则{
newValue=Double(字符串(子字符串[0])??0
}
}
如果self.numericValue!=newValue{
self.numericValue=newValue
}
}否则{
设newValue=Double(字符串(\(字符串(子字符串[0]))。\(St