英文:
How-to: UITextField wait until user stopped typing
问题
我正在尝试找到处理这个情况的最佳方法。我目前正在等待用户停止输入后再运行一些代码(延迟3秒),我打算使用textFieldDidEndEditing
,但正如我们所知,这仅在用户返回时执行,而不是在他们停止输入时执行。
目前我使用一个@IBAction
textFieldEditingChanged
,但这会在每输入一个字符/数字时执行代码。我尝试使用一个像shouldExecuteCode: Bool
这样的变量,但由于我在解析XML,响应几乎是立即的,代码仍然会执行多次。
我考虑使用某种OperationQueue
与一个操作或无并发性。
有没有人对此有好的想法?
当前实现
@IBAction func textFieldEditingChanged(_ sender: CurrencyTextField) {
self.viewModel.currentTextFieldIdentifier = sender.textFieldIdentifier
DispatchQueue.main.asyncAfter(deadline: .now() + viewModel.calculationDelay) {
if let text = sender.text,
let value = String.format(text) {
switch self.viewModel.currentTextFieldIdentifier {
case .fromCurrency:
self.calculateExchange(value: value,
valueCurrency: self.fromCurrencyTextField.currency,
outputCurrency: self.toCurrencyTextField.currency)
case .toCurrency:
self.calculateExchange(value: value,
valueCurrency: self.toCurrencyTextField.currency,
outputCurrency: self.fromCurrencyTextField.currency)
}
}
}
}
潜在实现
NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: sender)
.map({ ($0.object as? UITextField)?.text ?? "" })
.debounce(for: .milliseconds(500), scheduler: RunLoop.main)
.sink { [weak self] text in
guard let self = self else { return }
if let value = String.format(text) {
print(value)
switch self.viewModel.currencyTextFieldIdentifier {
case .fromCurrency:
self.calculateExchange(value: value,
valueCurrency: self.fromCurrencyTextField.currency,
outputCurrency: self.toCurrencyTextField.currency)
case .toCurrency:
self.calculateExchange(value: value,
valueCurrency: self.toCurrencyTextField.currency,
outputCurrency: self.fromCurrencyTextField.currency)
}
}
}
.store(in: &subscriptions)
英文:
I'm trying to find the best way to handle this. I'm currently looking to wait until user stops typing (let's say delay 3s) before running some code. Initially I was going to use textFieldDidEndEditing
but as we know this only executes when user returns, rather than when they stop typing.
Currently I use an @IBAction textFieldEditingChanged
but this will execute the code with each entered character/number. I tried to use a variable like shouldExecuteCode: Bool
but since I'm parsing an XML, the response is almost immediate and the code still get executed several times.
I was thinking to use some sort of OperationQueue
with one operation or no concurrency.
Anyone got a good idea for this?
Current Implementation
@IBAction func textFieldEditingChanged(_ sender: CurrencyTextField) {
self.viewModel.currentTextFieldIdentifier = sender.textFieldIdentifier
DispatchQueue.main.asyncAfter(deadline: .now() + viewModel.calculationDelay) {
if let text = sender.text,
let value = String.format(text) {
switch self.viewModel.currentTextFieldIdentifier {
case .fromCurrency:
self.calculateExchange(value: value,
valueCurrency: self.fromCurrencyTextField.currency,
outputCurrency: self.toCurrencyTextField.currency)
case .toCurrency:
self.calculateExchange(value: value,
valueCurrency: self.toCurrencyTextField.currency,
outputCurrency: self.fromCurrencyTextField.currency)
}
}
}
}
Potential Implementation
NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: sender)
.map({ ($0.object as? UITextField)?.text ?? "" })
.debounce(for: .milliseconds(500), scheduler: RunLoop.main)
.sink { [weak self] text in
guard let self = self else { return }
if let value = String.format(text) {
print(value)
switch self.viewModel.currencyTextFieldIdentifier {
case .fromCurrency:
self.calculateExchange(value: value,
valueCurrency: self.fromCurrencyTextField.currency,
outputCurrency: self.toCurrencyTextField.currency)
case .toCurrency:
self.calculateExchange(value: value,
valueCurrency: self.toCurrencyTextField.currency,
outputCurrency: self.fromCurrencyTextField.currency)
}
}
}
.store(in: &subscriptions)
答案1
得分: 1
以下是使用Combine框架的示例。这设置了两个简单的UITextFields,并设置了一个接收两者更改的通知,直到两个文本字段都没有发生更改3秒为止。
class ViewController: UIViewController {
var sub: AnyCancellable!
override func viewDidLoad() {
super.viewDidLoad()
let tf1 = UITextField(frame: CGRect(x: 70, y: 70, width: 150, height: 44))
tf1.borderStyle = .roundedRect
self.view.addSubview(tf1)
let tf2 = UITextField(frame: CGRect(x: 70, y: 120, width: 150, height: 44))
tf2.borderStyle = .roundedRect
self.view.addSubview(tf2)
sub = NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: nil)
.debounce(for: .seconds(3), scheduler: RunLoop.main)
.sink { _ in
print("\(Date()): tf1: \(tf1.text!), tf2: \(tf2.text!)")
}
}
}
您可以在文本字段中键入任何内容,切换到另一个文本字段并键入内容。只有在两个文本字段都停止输入3秒后,sink
块才会运行。
英文:
Here is an example using the Combine framework. This sets up two simple UITextFields and sets up one sink for both such that you don't get any notifications until no change has occurred in either text field for 3 seconds.
class ViewController: UIViewController {
var sub: AnyCancellable!
override func viewDidLoad() {
super.viewDidLoad()
let tf1 = UITextField(frame: CGRect(x: 70, y: 70, width: 150, height: 44))
tf1.borderStyle = .roundedRect
self.view.addSubview(tf1)
let tf2 = UITextField(frame: CGRect(x: 70, y: 120, width: 150, height: 44))
tf2.borderStyle = .roundedRect
self.view.addSubview(tf2)
sub = NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: nil)
.debounce(for: .seconds(3), scheduler: RunLoop.main)
.sink { _ in
print("\(Date()): tf1: \(tf1.text!), tf2: \(tf2.text!)")
}
}
}
You can type all you want into a text field, switch to the other and type. The sink
block will not run until you've stopped typing in both text fields for 3 seconds.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论