Swift 交互事件防抖的处理方案

727 阅读4分钟

当然,以下是几种在纯 Swift 项目中实现点击事件防抖的方案:

方案一:通过 DispatchQueue 实现防抖

使用 DispatchQueue 来实现防抖,设置一个时间间隔来忽略短时间内的重复点击。

import UIKit

class DebouncedButton: UIButton {
    private var isButtonDebounced = false
    private let debounceInterval: TimeInterval = 1.0

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupButton()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupButton()
    }

    private func setupButton() {
        addTarget(self, action: #selector(handleButtonClick), for: .touchUpInside)
    }

    @objc private func handleButtonClick() {
        guard !isButtonDebounced else { return }

        isButtonDebounced = true

        // Handle your button click action here
        print("Button clicked")

        DispatchQueue.main.asyncAfter(deadline: .now() + debounceInterval) {
            self.isButtonDebounced = false
        }
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let button = DebouncedButton(type: .system)
        button.setTitle("Click Me", for: .normal)
        button.frame = CGRect(x: 100, y: 100, width: 100, height: 50)
        button.backgroundColor = .blue
        button.setTitleColor(.white, for: .normal)
        self.view.addSubview(button)
    }
}

方案二:使用 Combine 框架实现防抖

如果你的项目中使用了 Combine 框架,可以利用 Combine 的 debounce 操作符来实现防抖。

import UIKit
import Combine

class DebouncedButton: UIButton {
    private var buttonClickSubject = PassthroughSubject<Void, Never>()
    private var cancellable: AnyCancellable?
    private let debounceInterval: TimeInterval = 1.0

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupButton()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupButton()
    }

    private func setupButton() {
        addTarget(self, action: #selector(handleButtonClick), for: .touchUpInside)
        setupDebounce()
    }

    private func setupDebounce() {
        cancellable = buttonClickSubject
            .debounce(for: .seconds(debounceInterval), scheduler: RunLoop.main)
            .sink { _ in
                self.handleDebouncedButtonClick()
            }
    }

    @objc private func handleButtonClick() {
        buttonClickSubject.send(())
    }

    private func handleDebouncedButtonClick() {
        // Handle your button click action here
        print("Button clicked")
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let button = DebouncedButton(type: .system)
        button.setTitle("Click Me", for: .normal)
        button.frame = CGRect(x: 100, y: 100, width: 100, height: 50)
        button.backgroundColor = .blue
        button.setTitleColor(.white, for: .normal)
        self.view.addSubview(button)
    }
}

方案三:使用自定义防抖函数

你可以创建一个通用的防抖函数,并在需要防抖的地方使用它。

import UIKit

class Debouncer {
    private var workItem: DispatchWorkItem?
    private let interval: TimeInterval

    init(interval: TimeInterval) {
        self.interval = interval
    }

    func run(action: @escaping () -> Void) {
        workItem?.cancel()
        workItem = DispatchWorkItem(block: action)
        DispatchQueue.main.asyncAfter(deadline: .now() + interval, execute: workItem!)
    }
}

class DebouncedButton: UIButton {
    private let debouncer = Debouncer(interval: 1.0)

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupButton()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupButton()
    }

    private func setupButton() {
        addTarget(self, action: #selector(handleButtonClick), for: .touchUpInside)
    }

    @objc private func handleButtonClick() {
        debouncer.run {
            self.handleDebouncedButtonClick()
        }
    }

    private func handleDebouncedButtonClick() {
        // Handle your button click action here
        print("Button clicked")
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let button = DebouncedButton(type: .system)
        button.setTitle("Click Me", for: .normal)
        button.frame = CGRect(x: 100, y: 100, width: 100, height: 50)
        button.backgroundColor = .blue
        button.setTitleColor(.white, for: .normal)
        self.view.addSubview(button)
    }
}

方案四:使用 @StateTimer

通过 Timer 来实现防抖。

import UIKit

class DebouncedButton: UIButton {
    private var isButtonDebounced = false
    private var timer: Timer? = nil
    private let debounceInterval: TimeInterval = 1.0

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupButton()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupButton()
    }

    private func setupButton() {
        addTarget(self, action: #selector(handleButtonClick), for: .touchUpInside)
    }

    @objc private func handleButtonClick() {
        guard !isButtonDebounced else { return }

        isButtonDebounced = true

        // Handle your button click action here
        print("Button clicked")

        timer?.invalidate()
        timer = Timer.scheduledTimer(withTimeInterval: debounceInterval, repeats: false) { _ in
            self.isButtonDebounced = false
        }
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let button = DebouncedButton(type: .system)
        button.setTitle("Click Me", for: .normal)
        button.frame = CGRect(x: 100, y: 100, width: 100, height: 50)
        button.backgroundColor = .blue
        button.setTitleColor(.white, for: .normal)
        self.view.addSubview(button)
    }
}

以上几种是纯 Swift 项目中实现点击事件防抖的方案都有各自的优点和场景,可以根据具体需求选择合适的方案。

当然可以使用 RxSwift 来处理点击事件的防抖。RxSwift 提供了丰富的操作符,可以轻松实现防抖效果。下面是一个使用 RxSwift 实现按钮点击事件防抖的示例。

使用 RxSwift 实现按钮点击事件防抖

首先,确保你已经在项目中添加了 RxSwift 和 RxCocoa,可以通过 CocoaPods、Carthage 或 Swift Package Manager 来添加依赖。

# 在 Podfile 中添加
pod 'RxSwift'
pod 'RxCocoa'

示例代码

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {
    let disposeBag = DisposeBag()
    let debounceInterval: RxTimeInterval = .seconds(1)

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = UIButton(type: .system)
        button.setTitle("Click Me", for: .normal)
        button.frame = CGRect(x: 100, y: 100, width: 100, height: 50)
        button.backgroundColor = .blue
        button.setTitleColor(.white, for: .normal)
        self.view.addSubview(button)

        // 使用 RxSwift 处理按钮点击事件
        button.rx.tap
            .debounce(debounceInterval, scheduler: MainScheduler.instance)
            .subscribe(onNext: { [weak self] in
                self?.handleButtonClick()
            })
            .disposed(by: disposeBag)
    }

    private func handleButtonClick() {
        // Handle your button click action here
        print("Button clicked")
    }
}

解释

  1. 导入库:确保你导入了 RxSwiftRxCocoa
  2. 初始化 DisposeBagDisposeBag 用于管理订阅的生命周期。
  3. 创建按钮:初始化按钮并将其添加到视图中。
  4. RxSwift 处理点击事件
    • 使用 button.rx.tap 创建一个点击事件的可观察对象。
    • 使用 debounce 操作符来设置防抖间隔,这里设置为 1 秒。
    • 订阅点击事件,当经过防抖间隔后执行按钮点击处理逻辑。
  5. 处理点击事件:在 handleButtonClick 方法中处理按钮点击的具体逻辑。

优点

使用 RxSwift 处理点击事件的防抖非常简洁、直观,并且可以轻松扩展到其他事件类型,比如手势、文本输入等。这种方法特别适合在 RxSwift 环境下的项目中使用。

如果你还不熟悉 RxSwift,建议阅读一些基础教程和文档,以便更好地理解和使用这个强大的响应式编程库。