Skip to content

Instantly share code, notes, and snippets.

@almostintuitive
Last active February 5, 2016 17:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save almostintuitive/7fa9aa7fe1bb1b65ae36 to your computer and use it in GitHub Desktop.
Save almostintuitive/7fa9aa7fe1bb1b65ae36 to your computer and use it in GitHub Desktop.
let pan = UIPanGestureRecognizer()
let pinch = UIPinchGestureRecognizer()
// Here, we want to create a signal that emits an event when the
// gesture has started.
// We can call .rx_event on UIPangestureRecognizer, and
// transform it's output into a signal, which then
// We can use filter to discard all other events!
let panStarted = pan.rx_event.filter { gesture in gesture.state == .Began }
// We do the same here, but this time we're only interested
// when the gestureRecognizer is in the .Ended state.
let panEnded = pan.rx_event.filter { gesture in gesture.state == .Ended }
let pinchStarted = pinch.rx_event.filter { gesture in gesture.state == .Began }
let pinchEnded = pinch.rx_event.filter { gesture in gesture.state == .Ended }
// Okay, let's think. Our aim is to only trigger the timer when both
// gestures began. We originally had to keep track of these states ourselves,
// but now that we transformed them into signals, we can also merge them, same
// way as you'd merge two arrays into one!
// Well, not exactly, we need a special combination, where the new signal will only
// start when both of its sub-signals have emitted an event: that's combineLatest().
let bothGesturesStarted = Observable.combineLatest(panStarted, pinchStarted) { (_, _) -> Bool in return true }
// For gestures ended, we need merge(), since we don't need to wait until both of
// them has started, we can immediately forward events from both channels into
// the combined one.
let bothGesturesEnded = Observable.of(panEnded, pinchEnded).merge()
// Now that we have our starting point, what should we do?
// Signals are special beasts: you can subscribe to them. Same way as
// you'd subscribe to a notification through NSNotificationCenter.
// subscribeNext will take a closure (block), and every time the signal
// fires, it'll run the signal's emitted value through the block!
bothGesturesStarted.subscribeNext { _ in
// So, when bothGesturesStarted started, do this:
print("started")
// Now we need to create a timer. Rx is a quite comprehensive library,
// it has some really handy structures and extensions - one of them is
// exactly a timer.
// What do we want? If we don't want to keep track of any more state,
// we can just create a signal that emits a number (Int), and increase it
// on every 'tick'.
let timer = Observable<Int>.timer(1, period: 1, scheduler: MainScheduler.instance)
// But wait, we don't want this timer to go on indefinitely!
// Hmm, actually this is quite easy. Rx will also provide you operations
// where you can limit the lifetime of a signal.
// Same way as you'd take only the first 3 items in the array, you can
// use take(x) to only take the first x events (values) from a signal.
let timerThatTicksThree = timer.take(3)
// And finally, we can also combine signals in other ways: this time,
// we need to stop the timer signal when another signal is fired.
// Here, we just use takeUntil! It's like the "while" loop of signals.
let timerThatTicksThreeAndStops = timerThatTicksThree.takeUntil(bothGesturesEnded)
timerThatTicksThreeAndStops.subscribe(onNext: { count in
// when a tick happens, do this:
print("tick: \(count)")
}, onError: nil, onCompleted: {
// when the timer completes, do this:
print("completed")
}, onDisposed: nil)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment