Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: How to ensure only a single tap is responded to on a UIButton? #3592

Closed
nasht opened this issue May 4, 2018 · 2 comments
Closed

Comments

@nasht
Copy link

nasht commented May 4, 2018

Apologies if this is obvious - I'm still relatively new to ReactiveSwift...

Right now I have some code like this

myButton.reactive
            .controlEvents(.touchUpInside)
            .observeValues  { 
 <some async operation here>
}

If I tap the button multiple times, the code above will reasonably run for each tap.

What is the best (reactive) way to disregard multiple taps, and respond to a single tap only? I can solve this using semaphores, and setting the button disabled / enabled, but surely there is a more elegant way to do it?

I've tried using debounce(X) but that lets me keep tapping the button for a while, and can still sometimes yield multiple taps.

Also to be clear, I don't want the signal to run only once, so skipDuplicates() doesn't seem quite right either...

Many thanks!

@sharplet
Copy link
Contributor

sharplet commented May 4, 2018

Is your goal to prevent the async code from running multiple times in parallel? The way I'd recommend doing that is to wrap your async work in a SignalProducer, and then use an Action to bind it to your button.

First define your async work in a function that returns a new SignalProducer:

func work() -> SignalProducer<Foo, MyError> {
  // ...
}

Then wrap it in an action:

let workAction = Action(execute: work) // Action<(), Foo, MyError>

Now you can bind it to your button:

func viewDidLoad() {
  super.viewDidLoad()

  workButton.reactive.pressed = CocoaAction(workAction)
}

Whenever the button is tapped, the action will be executed. The button's isEnabled property is also bound to the action's isEnabled property, meaning that it is disabled while the work is executing. You can read a bit more about Action here: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/ReactivePrimitives.md#action-a-serialized-worker-with-a-preset-action.

There are other ways to compose signals and signal producers, but for the purposes of binding work to a button, Action is a really useful tool. (For example, it allows you to display/handle errors separately via its errors signal.)

@RuiAAPeres
Copy link
Member

Hello. 👋 Thanks for opening this issue. Due to inactivity, we will soft close the issue. If you feel that it should remain open, please let us know. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants