Par*_*ris 22 throttling ios swift
我正在尝试用a UISearchView来查询谷歌的地方.在这样做的时候,对于我的文本更改调用UISearchBar,我正在向google地方发出请求.问题是我宁愿将此调用去抖动,每250毫秒只请求一次,以避免不必要的网络流量.我不想自己写这个功能,但如果需要,我会.
我发现:https://gist.github.com/ShamylZakariya/54ee03228d955f458389,但我不太确定如何使用它:
func debounce( delay:NSTimeInterval, #queue:dispatch_queue_t, action: (()->()) ) -> ()->() {
var lastFireTime:dispatch_time_t = 0
let dispatchDelay = Int64(delay * Double(NSEC_PER_SEC))
return {
lastFireTime = dispatch_time(DISPATCH_TIME_NOW,0)
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
dispatchDelay
),
queue) {
let now = dispatch_time(DISPATCH_TIME_NOW,0)
let when = dispatch_time(lastFireTime, dispatchDelay)
if now >= when {
action()
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
以下是我尝试使用上述代码的一件事:
let searchDebounceInterval: NSTimeInterval = NSTimeInterval(0.25)
func findPlaces() {
// ...
}
func searchBar(searchBar: UISearchBar!, textDidChange searchText: String!) {
debounce(
searchDebounceInterval,
dispatch_get_main_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT),
self.findPlaces
)
}
Run Code Online (Sandbox Code Playgroud)
产生的错误是 Cannot invoke function with an argument list of type '(NSTimeInterval, $T5, () -> ())
我如何使用此方法,或者有更好的方法在iOS/Swift中执行此操作.
Khr*_*rob 28
对于那些不想创建类/扩展的人来说,这是一个选项:
在您的代码中的某处:
var debounce_timer:Timer?
Run Code Online (Sandbox Code Playgroud)
在你想要做去抖的地方:
debounce_timer?.invalidate()
debounce_timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { _ in
print ("Debounce this...")
}
Run Code Online (Sandbox Code Playgroud)
mat*_*att 16
将它放在文件的顶层,以免混淆Swift的有趣参数名称规则.请注意,我已删除了#所有参数,因此现在没有任何参数具有名称:
func debounce( delay:NSTimeInterval, queue:dispatch_queue_t, action: (()->()) ) -> ()->() {
var lastFireTime:dispatch_time_t = 0
let dispatchDelay = Int64(delay * Double(NSEC_PER_SEC))
return {
lastFireTime = dispatch_time(DISPATCH_TIME_NOW,0)
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
dispatchDelay
),
queue) {
let now = dispatch_time(DISPATCH_TIME_NOW,0)
let when = dispatch_time(lastFireTime, dispatchDelay)
if now >= when {
action()
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,在您的实际课程中,您的代码将如下所示:
let searchDebounceInterval: NSTimeInterval = NSTimeInterval(0.25)
let q = dispatch_get_main_queue()
func findPlaces() {
// ...
}
let debouncedFindPlaces = debounce(
searchDebounceInterval,
q,
findPlaces
)
Run Code Online (Sandbox Code Playgroud)
现在debouncedFindPlaces是一个你可以调用的函数,findPlaces除非delay你上次调用它,否则你将不会被执行.
d4R*_*4Rk 16
func debounce(interval: Int, queue: DispatchQueue, action: @escaping (() -> Void)) -> () -> Void {
var lastFireTime = DispatchTime.now()
let dispatchDelay = DispatchTimeInterval.milliseconds(interval)
return {
lastFireTime = DispatchTime.now()
let dispatchTime: DispatchTime = DispatchTime.now() + dispatchDelay
queue.asyncAfter(deadline: dispatchTime) {
let when: DispatchTime = lastFireTime + dispatchDelay
let now = DispatchTime.now()
if now.rawValue >= when.rawValue {
action()
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
有时让debounce函数采用参数是有用的.
typealias Debounce<T> = (_ : T) -> Void
func debounce<T>(interval: Int, queue: DispatchQueue, action: @escaping Debounce<T>) -> Debounce<T> {
var lastFireTime = DispatchTime.now()
let dispatchDelay = DispatchTimeInterval.milliseconds(interval)
return { param in
lastFireTime = DispatchTime.now()
let dispatchTime: DispatchTime = DispatchTime.now() + dispatchDelay
queue.asyncAfter(deadline: dispatchTime) {
let when: DispatchTime = lastFireTime + dispatchDelay
let now = DispatchTime.now()
if now.rawValue >= when.rawValue {
action(param)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
在下面的示例中,您可以看到debouncing如何工作,使用字符串参数来标识调用.
let debouncedFunction = debounce(interval: 200, queue: DispatchQueue.main, action: { (identifier: String) in
print("called: \(identifier)")
})
DispatchQueue.global(qos: .background).async {
debouncedFunction("1")
usleep(100 * 1000)
debouncedFunction("2")
usleep(100 * 1000)
debouncedFunction("3")
usleep(100 * 1000)
debouncedFunction("4")
usleep(300 * 1000) // waiting a bit longer than the interval
debouncedFunction("5")
usleep(100 * 1000)
debouncedFunction("6")
usleep(100 * 1000)
debouncedFunction("7")
usleep(300 * 1000) // waiting a bit longer than the interval
debouncedFunction("8")
usleep(100 * 1000)
debouncedFunction("9")
usleep(100 * 1000)
debouncedFunction("10")
usleep(100 * 1000)
debouncedFunction("11")
usleep(100 * 1000)
debouncedFunction("12")
}
Run Code Online (Sandbox Code Playgroud)
注意:该usleep()功能仅用于演示目的,可能不是真正应用程序的最佳解决方案.
当自上次呼叫以来至少有200ms的间隔时,您始终会收到回叫.
叫:4
叫:7
叫:12
qui*_*yme 14
尽管这里有几个很好的答案,但我想我会分享我最喜欢的(纯 Swift)方法来消除用户输入的搜索......
1)添加这个简单的类(Debounce.swift):
import Dispatch
class Debounce<T: Equatable> {
private init() {}
static func input(_ input: T,
comparedAgainst current: @escaping @autoclosure () -> (T),
perform: @escaping (T) -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if input == current() { perform(input) }
}
}
}
Run Code Online (Sandbox Code Playgroud)
2) 可选择包含此单元测试 ( DebounceTests.swift ):
import XCTest
class DebounceTests: XCTestCase {
func test_entering_text_delays_processing_until_settled() {
let expect = expectation(description: "processing completed")
var finalString: String = ""
var timesCalled: Int = 0
let process: (String) -> () = {
finalString = $0
timesCalled += 1
expect.fulfill()
}
Debounce<String>.input("A", comparedAgainst: "AB", perform: process)
Debounce<String>.input("AB", comparedAgainst: "ABCD", perform: process)
Debounce<String>.input("ABCD", comparedAgainst: "ABC", perform: process)
Debounce<String>.input("ABC", comparedAgainst: "ABC", perform: process)
wait(for: [expect], timeout: 2.0)
XCTAssertEqual(finalString, "ABC")
XCTAssertEqual(timesCalled, 1)
}
}
Run Code Online (Sandbox Code Playgroud)
3)在任何你想延迟处理的地方使用它(例如UISearchBarDelegate):
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
Debounce<String>.input(searchText, comparedAgainst: searchBar.text ?? "") {
self.filterResults($0)
}
}
Run Code Online (Sandbox Code Playgroud)
基本前提是我们只是将输入文本的处理延迟了 0.5 秒。那时,我们将我们从事件中得到的字符串与搜索栏的当前值进行比较。如果它们匹配,我们假设用户已暂停输入文本,然后继续过滤操作。
因为它是通用的,所以它适用于任何类型的等值值。
由于 Dispatch 模块从第 3 版起就包含在 Swift 核心库中,因此该类也可以安全地用于非 Apple 平台。
小智 9
如果您想保持整洁,这是一个基于GCD的解决方案,可以使用熟悉的基于GCD的语法来完成您需要的工作:https : //gist.github.com/staminajim/b5e89c6611eef81910502db2a01f1a83
DispatchQueue.main.asyncDeduped(target: self, after: 0.25) { [weak self] in
self?.findPlaces()
}
Run Code Online (Sandbox Code Playgroud)
在上一次调用asyncDuped之后的0.25秒之后,只会一次调用一次 findPlaces()。
我使用了这个受 Objective-C 启发的古老方法:
override func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
// Debounce: wait until the user stops typing to send search requests
NSObject.cancelPreviousPerformRequests(withTarget: self)
perform(#selector(updateSearch(with:)), with: searchText, afterDelay: 0.5)
}
Run Code Online (Sandbox Code Playgroud)
注意,被调用的方法updateSearch必须标记@objc!
@objc private func updateSearch(with text: String) {
// Do stuff here
}
Run Code Online (Sandbox Code Playgroud)
这种方法的一大优点是我可以传递参数(此处:搜索字符串)。对于这里介绍的大多数去抖动器来说,情况并非如此......
首先,创建一个Debouncer泛型类:
//
// Debouncer.swift
//
// Created by Frédéric Adda
import UIKit
import Foundation
class Debouncer {
// MARK: - Properties
private let queue = DispatchQueue.main
private var workItem = DispatchWorkItem(block: {})
private var interval: TimeInterval
// MARK: - Initializer
init(seconds: TimeInterval) {
self.interval = seconds
}
// MARK: - Debouncing function
func debounce(action: @escaping (() -> Void)) {
workItem.cancel()
workItem = DispatchWorkItem(block: { action() })
queue.asyncAfter(deadline: .now() + interval, execute: workItem)
}
}
Run Code Online (Sandbox Code Playgroud)
然后创建使用去抖动机制的UISearchBar的子类:
//
// DebounceSearchBar.swift
//
// Created by Frédéric ADDA on 28/06/2018.
//
import UIKit
/// Subclass of UISearchBar with a debouncer on text edit
class DebounceSearchBar: UISearchBar, UISearchBarDelegate {
// MARK: - Properties
/// Debounce engine
private var debouncer: Debouncer?
/// Debounce interval
var debounceInterval: TimeInterval = 0 {
didSet {
guard debounceInterval > 0 else {
self.debouncer = nil
return
}
self.debouncer = Debouncer(seconds: debounceInterval)
}
}
/// Event received when the search textField began editing
var onSearchTextDidBeginEditing: (() -> Void)?
/// Event received when the search textField content changes
var onSearchTextUpdate: ((String) -> Void)?
/// Event received when the search button is clicked
var onSearchClicked: (() -> Void)?
/// Event received when cancel is pressed
var onCancel: (() -> Void)?
// MARK: - Initializers
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
delegate = self
}
override init(frame: CGRect) {
super.init(frame: frame)
delegate = self
}
override func awakeFromNib() {
super.awakeFromNib()
delegate = self
}
// MARK: - UISearchBarDelegate
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
onCancel?()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
onSearchClicked?()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
onSearchTextDidBeginEditing?()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
guard let debouncer = self.debouncer else {
onSearchTextUpdate?(searchText)
return
}
debouncer.debounce {
DispatchQueue.main.async {
self.onSearchTextUpdate?(self.text ?? "")
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,此类设置为UISearchBarDelegate。动作将作为闭包传递给此类。
最后,您可以像这样使用它:
class MyViewController: UIViewController {
// Create the searchBar as a DebounceSearchBar
// in code or as an IBOutlet
private var searchBar: DebounceSearchBar?
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar = createSearchBar()
}
private func createSearchBar() -> DebounceSearchBar {
let searchFrame = CGRect(x: 0, y: 0, width: 375, height: 44)
let searchBar = DebounceSearchBar(frame: searchFrame)
searchBar.debounceInterval = 0.5
searchBar.onSearchTextUpdate = { [weak self] searchText in
// call a function to look for contacts, like:
// searchContacts(with: searchText)
}
searchBar.placeholder = "Enter name or email"
return searchBar
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,在这种情况下,DebounceSearchBar已经是searchBar委托。你应该不设置这个UIViewController子类的搜索栏委托!也不要使用委托函数。请使用提供的闭包!
| 归档时间: |
|
| 查看次数: |
13098 次 |
| 最近记录: |