damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

DebouncedOnChange.swift (2815B)


      1 // https://github.com/Tunous/DebouncedOnChange/blob/5670ea13e8ad33e9cc3197f6d13ce492dc0e46ab/Sources/DebouncedOnChange/DebouncedChangeViewModifier.swift
      2 
      3 import SwiftUI
      4 import Foundation
      5 
      6 extension View {
      7 
      8     /// Adds a modifier for this view that fires an action only when a time interval in seconds represented by
      9     /// `debounceTime` elapses between value changes.
     10     ///
     11     /// Each time the value changes before `debounceTime` passes, the previous action will be cancelled and the next
     12     /// action /// will be scheduled to run after that time passes again. This mean that the action will only execute
     13     /// after changes to the value /// stay unmodified for the specified `debounceTime` in seconds.
     14     ///
     15     /// - Parameters:
     16     ///   - value: The value to check against when determining whether to run the closure.
     17     ///   - debounceTime: The time in seconds to wait after each value change before running `action` closure.
     18     ///   - action: A closure to run when the value changes.
     19     /// - Returns: A view that fires an action after debounced time when the specified value changes.
     20     public func onChange<Value>(
     21         of value: Value,
     22         debounceTime: TimeInterval,
     23         perform action: @escaping (_ newValue: Value) -> Void
     24     ) -> some View where Value: Equatable {
     25         self.modifier(DebouncedChangeViewModifier(trigger: value, debounceTime: debounceTime, action: action))
     26     }
     27 }
     28 
     29 private struct DebouncedChangeViewModifier<Value>: ViewModifier where Value: Equatable {
     30     let trigger: Value
     31     let debounceTime: TimeInterval
     32     let action: (Value) -> Void
     33 
     34     @State private var debouncedTask: Task<Void, Never>?
     35 
     36     func body(content: Content) -> some View {
     37         content.onChange(of: trigger) { value in
     38             debouncedTask?.cancel()
     39             debouncedTask = Task.delayed(seconds: debounceTime) { @MainActor in
     40                 action(value)
     41             }
     42         }
     43     }
     44 }
     45 
     46 extension Task {
     47 
     48     /// Asynchronously runs the given `operation` in its own task after the specified number of `seconds`.
     49     ///
     50     /// The operation will be executed after specified number of `seconds` passes. You can cancel the task earlier
     51     /// for the operation to be skipped.
     52     ///
     53     /// - Parameters:
     54     ///   - time: Delay time in seconds.
     55     ///   - operation: The operation to execute.
     56     /// - Returns: Handle to the task which can be cancelled.
     57     @discardableResult
     58     public static func delayed(
     59         seconds: TimeInterval,
     60         operation: @escaping @Sendable () async -> Void
     61     ) -> Self where Success == Void, Failure == Never {
     62         Self {
     63             do {
     64                 try await Task<Never, Never>.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
     65                 await operation()
     66             } catch {}
     67         }
     68     }
     69 }