ios - 如何创建只接受数字和一个点的 SwiftUI TextField?

标签 ios textfield swiftui

如何创建一个只允许用户输入数字和一个点的 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/

    相关文章:

    iphone - MKMapView renderInContext - iPhone 4 (Retina) 问题

    ios - 如何使用 swift 和 UIkit 创建逼真的悬崖坠落球

    html - 在 Chrome 扩展中禁用文本字段蓝色突出显示?

    preview - 如何让预览在 SwiftUI 项目中再次工作

    ios - List 对象的 SwiftUI 分页

    swift - 将参数传递给 SwiftUI 工作表

    ios - 即使方法确实存在,也无法识别的选择器发送到实例

    ios - 试图将一个简单的 OBJC 函数转换为 Swift

    javascript - Regex.test(value) 在记录时返回 true,但在 if 语句中返回 false

    Flutter - TextField 在焦点上重建小部件