skip to Main Content

I have a custom class of UIButton. I want to add delay on action if user tap on button. I can use following code to each and every button action but I want to use generic solution so that I need not to visit each and every button action places.

 @IBAction func buttonTapped(_ sender: CustomButton) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.4, execute: {
            // do action
        })
    }

2

Answers


  1. You can pass the action in to the custom class as a closure. You then just need to link the IBAction up in storyboard or you can do programatically in the button init with addTarget if not using storyboard. I assume you have an init in your custom class setting up other properties on the button.

    class CustomButton: UIButton {
        
        var buttonAction: () -> Void
        
        init(frame: CGRect , _ buttonAction: @escaping () -> Void ) {
            self.buttonAction = buttonAction
            super.init(frame: frame)
            // This code below passes the action in if you are not using @IBAction / Storyboard
            // self.addTarget(self , action: #selector(delayTapAction), for: .touchUpInside)
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        @IBAction func buttonTapped(_ sender: CustomButton) {
            delayTapAction()
        }
        
        @objc func delayTapAction() {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.4, execute: {
                self.buttonAction()
                print("Action Ran!")
            })
        }
        
    }
    

    Calling programatically you can pass the action in as a closure as follows. Be wary if you are running logic that may capture the view controller self and use weak or unowned if actions could run a more than a short response in async.

    class TestVC: UIViewController {
        
        override func viewDidLoad() {
            let button = CustomButton(frame: CGRect(x: 0, y: 0, width: 100, height: 32)) {
                print("Action prints after delay")
            }
            self.view.addSubview(button)
        }
    }
    
    Login or Signup to reply.
  2. A rather generic way is to use Combine.

    • Implement an extension of UIControl described in the Medium article Observe UIButton Events Using Combine in Swift 5 to be able to observe any button event, the full code is available on GitHub.

    • Add this extension of UIButton, the delay operator does what its name implies.

      extension UIButton {
           var tapDelayPublisher : AnyPublisher<Void, Never> {
              return self
                   .publisher(for: .touchUpInside)
                   .delay(for: .milliseconds(400), scheduler: DispatchQueue.main)
                   .eraseToAnyPublisher()
           }
      }
      
    • Observe the button(s)

      var subscriptions = Set<AnyCancellable>()
      
      // ...
      button.tapDelayPublisher
          .sink { _ in
              print("Button tapped)
          }
          .store(in: &subscriptions)
      
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search