You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS按钮防重复点击函数修复及全局扩展实现咨询

Fix for One Tap Per Second Function & Global Reusable Extension

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 18:31:49