Swift 4 解决方案,以及一些通用的 cmets:
这些都是合理的方法,但是如果您想要示范性的自动搜索行为,您确实需要两个单独的计时器或调度。
理想的行为是 1) 定期触发自动搜索,但 2) 不会太频繁(因为服务器负载、蜂窝带宽以及可能导致 UI 卡顿),以及 3) 一旦有用户输入暂停。
您可以使用一个较长时间的计时器来实现此行为,该计时器在编辑开始后立即触发(我建议 2 秒)并且无论以后的活动如何都允许运行,再加上一个短期计时器(约 0.75 秒),即在每次更改时重置。任何一个计时器到期都会触发自动搜索并重置两个计时器。
最终的结果是,连续键入会在较长的几秒内产生一次自动搜索,但保证在短时间内会触发一次自动搜索。
您可以使用下面的 AutosearchTimer 类非常简单地实现此行为。使用方法如下:
// The closure specifies how to actually do the autosearch
lazy var timer = AutosearchTimer { [weak self] in self?.performSearch() }
// Just call activate() after all user activity
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
timer.activate()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
performSearch()
}
func performSearch() {
timer.cancel()
// Actual search procedure goes here...
}
AutosearchTimer 在释放时会自行处理清理工作,因此您无需在自己的代码中担心这一点。但是不要给计时器一个对 self 的强引用,否则你会创建一个引用循环。
下面的实现使用了计时器,但如果您愿意,您可以根据调度操作对其进行重铸。
// Manage two timers to implement a standard autosearch in the background.
// Firing happens after the short interval if there are no further activations.
// If there is an ongoing stream of activations, firing happens at least
// every long interval.
class AutosearchTimer {
let shortInterval: TimeInterval
let longInterval: TimeInterval
let callback: () -> Void
var shortTimer: Timer?
var longTimer: Timer?
enum Const {
// Auto-search at least this frequently while typing
static let longAutosearchDelay: TimeInterval = 2.0
// Trigger automatically after a pause of this length
static let shortAutosearchDelay: TimeInterval = 0.75
}
init(short: TimeInterval = Const.shortAutosearchDelay,
long: TimeInterval = Const.longAutosearchDelay,
callback: @escaping () -> Void)
{
shortInterval = short
longInterval = long
self.callback = callback
}
func activate() {
shortTimer?.invalidate()
shortTimer = Timer.scheduledTimer(withTimeInterval: shortInterval, repeats: false)
{ [weak self] _ in self?.fire() }
if longTimer == nil {
longTimer = Timer.scheduledTimer(withTimeInterval: longInterval, repeats: false)
{ [weak self] _ in self?.fire() }
}
}
func cancel() {
shortTimer?.invalidate()
longTimer?.invalidate()
shortTimer = nil; longTimer = nil
}
private func fire() {
cancel()
callback()
}
}