如何阻止外部HID扫描器向SwiftUI搜索栏TextField连续追加扫描值
问题原因
HID扫描器本质是模拟键盘输入,默认不会主动清空搜索框已有内容,而你的搜索框持续处于第一响应者状态,光标始终停留在文本末尾,新输入的条码字符会直接追加到原有内容之后,就会出现你遇到的重复拼接问题。
具体修改方案
1. 新增扫描输入判定逻辑
绝大多数HID扫描器出厂默认会在条码输入完成后自动发送回车符,同时扫描器输入字符的间隔远快于人工打字,我们可以通过这两个特征区分扫描输入和手动输入,实现新扫描内容直接替换旧内容。首先修改SearchBar的Coordinator代码:
class Coordinator: NSObject, UISearchBarDelegate { var onTextChanged: (String) -> Void @Binding var text: String // 新增属性:记录上次输入时间,区分扫描/手动输入 private var lastInputTime: TimeInterval = 0 private let scannerInputThreshold: TimeInterval = 0.05 // 扫描器单字符输入间隔通常小于50ms init(text: Binding<String>, onTextChanged: @escaping (String) -> Void) { _text = text self.onTextChanged = onTextChanged } // 新增文本变更前的拦截代理 func searchBar(_ searchBar: UISearchBar, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { let currentTime = Date().timeIntervalSince1970 defer { lastInputTime = currentTime } // 判定1:收到回车符,说明单次扫描完成 if text == "\n" { if let query = searchBar.text, !query.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { onTextChanged(query) } // 可选:如果不需要保留扫描结果在输入框,可直接清空,下次扫描直接输入新内容 // searchBar.text = "" // self.text = "" return false } // 判定2:输入间隔小于阈值,判定为扫描器输入,直接替换全部原有内容 if currentTime - lastInputTime < scannerInputThreshold && range.location == 0 { searchBar.text = text self.text = text return false } return true } // 原有textDidChange逻辑无需大幅调整 func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { text = searchText NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(self.reload(_:)), object: searchBar) perform(#selector(self.reload(_:)), with: searchBar, afterDelay: 0.75) } @objc func reload(_ searchBar: UISearchBar) { guard let query = searchBar.text, query.trimmingCharacters(in: .whitespaces) != "" else { print("nothing to search") return } print(query) } }
2. 优化第一响应者设置逻辑
你当前的代码每次刷新视图都会重复调用becomeFirstResponder,会导致光标始终固定在文本末尾,调整为仅初始化时设置一次即可:
func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) { uiView.text = text // 只在首次加载时设置第一响应者 if context.coordinator.lastInputTime == 0 { uiView.becomeFirstResponder() } }
内容的提问来源于stack exchange,提问作者Lyndon Abesamis
相关产品推荐
相关产品推荐

