Debouncify 🔂

.package(url: "https://github.com/mesqueeb/Debouncify", from: "0.1.0")

“防抖”是指在短时间内延迟执行一个函数。如果在该时间内该函数被调用两次,则该函数仅在最后一次调用后经过指定的持续时间后执行一次。

SwiftUI onChangeDebounced View 修饰符

假设您有一个函数,您希望在每次按键时执行,但希望通过 300 毫秒进行防抖,以便仅在用户停止输入一段时间后才执行它。

示例

@State private var query: String = ""

func search(_ query: String) async {
    print("searching!")
    // your search API call logic...
}

var body: some View {
    TextField("Search...", text: $query)
        .onChangeDebounced(of: query, after: .milliseconds(300)) { _oldValue, newValue in
            search(newValue)
        }
}

取消防抖任务

如果您需要取消搜索函数的防抖执行,例如当用户按下 ESC 键时,您可以传递一个包含 Task 的 Binding,然后您可以取消该 Task

示例

@State private var query: String = ""

func search(_ query: String) async {
    print("searching!")
    // your search API call logic...
}

/// The search Task is added by `onChangeDebounced` below
@State private var searchTask: Task<Void, never>? = nil

var body: some View {
    TextField("Search...", text: $query)
    .onChangeDebounced(of: query, after: .milliseconds(300), task: $searchTask) { _oldValue, newValue in
        search(newValue)
    }
    .onKeyPress(.return) {
        searchTask?.cancel()
        search(query)
        return .handled
    }
    .onKeyPress(.escape) {
        searchTask?.cancel()
        return .handled
    }
}

Swift Debouncify Actor

使用 Debouncify 包装一个函数,它会自动在每次后续调用时进行防抖处理。

示例

// Example debounced function
func search() async {
    print("searching!")
    // your search API call logic...
}

// Using Debouncify to wrap the function
let searchAfter300ms = Debouncify(call: search, after: .milliseconds(300))

// Usage
Task {
    Task { await searchAfter300ms() }
    try await Task.sleep(for: .milliseconds(100))
    Task { await searchAfter300ms() }
    try await Task.sleep(for: .milliseconds(100))
    Task { await searchAfter300ms() }
}
// it will only print "searching!" once after 300ms

取消防抖任务

您可以通过调用 Debouncify 实例上的 cancel 方法来取消防抖任务。

示例

// Example debounced function
func search() async {}

let searchAfter300ms = Debouncify(by: .milliseconds(300), search)
var task: Task<Any, Any>? = nil

Task { await searchAfter300ms() }
// if the search needs to be cancelled before the Task above finishes
Task { await searchAfter300ms.cancel() }

文档

有关更多信息,请参阅文档