iOS按钮防重复点击函数修复及全局扩展实现咨询
Why Your Original Function Fails
The onlyOneTap function doesn’t work because isAvailableToTap is a local variable—every time you call the function, it initializes a new true value, so the completion always runs. We need to persist the tap-allowed state between calls.
Solution 1: UIButton Extension (Button-Specific)
This extension adds a reusable throttling method directly to UIButton, storing the tap state per button instance using associated objects.
import UIKit import ObjectiveC // Associated object key to store tap availability state private var TapAvailabilityKey: UInt8 = 0 extension UIButton { // Private property to track if the button can be tapped private var isAvailableToTap: Bool { get { return objc_getAssociatedObject(self, &TapAvailabilityKey) as? Bool ?? true } set { objc_setAssociatedObject(self, &TapAvailabilityKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } /// Executes the completion only once every specified delay (default: 1 second) /// - Parameters: /// - delay: Minimum time between allowed taps /// - completion: Closure to run when tap is allowed func onlyOneTap(delay: TimeInterval = 1.0, completion: @escaping () -> Void) { guard isAvailableToTap else { return } isAvailableToTap = false completion() DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in self?.isAvailableToTap = true } } }
How to Use
Call this method from your button’s tap handler:
// Example: Add tap target to your button yourButton.addTarget(self, action: #selector(handleExitTap), for: .touchUpInside) @objc private func handleExitTap(_ sender: UIButton) { sender.onlyOneTap { interactor.exitProfile() } }
Solution 2: Generic Throttled Closure (Any Scenario)
If you need throttling for non-button closures (e.g., gesture recognizers, custom actions), use this function to create a reusable throttled closure. The state is captured and persisted in the returned closure.
/// Creates a throttled closure that executes only once every specified delay /// - Parameters: /// - delay: Minimum time between allowed executions /// - closure: Original closure to throttle /// - Returns: Throttled version of the input closure func makeThrottledClosure(delay: TimeInterval = 1.0, closure: @escaping () -> Void) -> () -> Void { var isAvailable = true return { guard isAvailable else { return } isAvailable = false closure() DispatchQueue.main.asyncAfter(deadline: .now() + delay) { isAvailable = true } } }
How to Use
Store the throttled closure as a property in your view controller, then call it when needed:
// Create the throttled closure once (e.g., in viewDidLoad) private let throttledExit = makeThrottledClosure { interactor.exitProfile() } // Call it from your tap handler @objc private func handleExitTap() { throttledExit() }
内容的提问来源于stack exchange,提问作者Vadim

