-
Notifications
You must be signed in to change notification settings - Fork 3
Short reactive gesture reactor #13
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
Open
fabb
wants to merge
8
commits into
master
Choose a base branch
from
short_reactive_gesture_reactor
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
4a99e42
added IntegratedReactiveViewController and IntegratedReactiveGestureR…
fabb 71d9d57
added IntegratedReactiveGestureReactorTests, does not yet compile - m…
fabb d3c73b3
exchanged events with observables in order to be able to directly pro…
fabb bb5c96e
modify IntegratedReactiveGestureReactorTests to compile, still 2 test…
fabb 0bf5d97
Merge branch 'master' into short_reactive_gesture_reactor
fabb 37fb919
fixed typo and wrong comments
fabb 7c5286f
Merge branch 'master' into short_reactive_gesture_reactor
fabb 8d28337
fixed af41ec9 in IntegratedReactiveGestureReactor too
fabb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
functional-reactive-intuition/Project/RFP/IntegratedReactiveGestureReactor.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import Foundation | ||
| import UIKit | ||
| import RxSwift | ||
| import RxCocoa | ||
|
|
||
|
|
||
| // same implementation as ReactiveGestureReactor, but not using GestureReactor protocol, rather directly using Rx events | ||
| class IntegratedReactiveGestureReactor { | ||
|
|
||
| var delegate: GestureReactorDelegate? | ||
|
|
||
| private let timerCreator: ReactiveTimerCreator | ||
| private let disposeBag = DisposeBag() | ||
|
|
||
| init(timerCreator: ReactiveTimerCreator, panGestureObservable: Observable<UIGestureRecognizerType>, rotateGestureObservable: Observable<UIGestureRecognizerType>) { | ||
|
|
||
| self.timerCreator = timerCreator | ||
|
|
||
| // FYI | ||
| // Passing on the UIGesture at this point is dodgy as it's a reference | ||
| // It's state will change and render our filter useless. | ||
| // We therefore keep just the state in our observable buffers [.Began,.Began,.Ended] | ||
| let rotateGesturesStartedEnded = rotateGestureObservable.filter { gesture in gesture.state == .Began || gesture.state == .Ended}.flatMap { (gesture) -> Observable<UIGestureRecognizerState> in | ||
| return Observable.just(gesture.state) | ||
| } | ||
|
|
||
| let panGesturesStartedEnded = panGestureObservable.filter { gesture in gesture.state == .Began || gesture.state == .Ended}.flatMap { (gesture) -> Observable<UIGestureRecognizerState> in | ||
| return Observable.just(gesture.state) | ||
| } | ||
|
|
||
| // Combine our latest .Began and .Ended from both Pan and Rotate. | ||
| // If they are the same then return the same state. If not then return a Failed. | ||
| let combineStartEndGestures = Observable.combineLatest(panGesturesStartedEnded, rotateGesturesStartedEnded) { (panState, rotateState) -> Observable<UIGestureRecognizerState> in | ||
|
|
||
| // If only one is .Ended, the result is .Ended too | ||
| var state = UIGestureRecognizerState.Ended | ||
| if panState == .Began && rotateState == .Began { | ||
| state = .Began | ||
| } | ||
|
|
||
| return Observable.just(state) | ||
| }.switchLatest() | ||
|
|
||
| // several .Began events in a row are to be treated the same as a single one, it has just meaning if a .Ended is in between | ||
| let distinctCombineStartEndGestures = combineStartEndGestures.distinctUntilChanged() | ||
|
|
||
|
|
||
| // condition: when both pan and rotate has begun | ||
| let bothGesturesStarted = distinctCombineStartEndGestures.filter { (state) -> Bool in | ||
| state == .Began | ||
| } | ||
|
|
||
| // condition: when one of pan or rotate has Ended | ||
| let eitherGesturesEnded = distinctCombineStartEndGestures.filter { (state) -> Bool in | ||
| state == .Ended | ||
| } | ||
|
|
||
| // when bothGesturesStarted, do this: | ||
| bothGesturesStarted.subscribeNext { [unowned self] _ in | ||
|
|
||
| self.delegate?.didStart() | ||
| // create a timer that ticks every second | ||
| let timer = self.timerCreator(interval: 1) | ||
| // condition: but only three ticks | ||
| let timerThatTicksThree = timer.take(4) | ||
| // condition: and also, stop it immediately either pan or rotate ended | ||
| let timerThatTicksThreeAndStops = timerThatTicksThree.takeUntil(eitherGesturesEnded) | ||
|
|
||
| timerThatTicksThreeAndStops.subscribe(onNext: { [unowned self] count in | ||
| // the imperative version waits for a second until didComplete is called, so we have to tick once more, but do not send the last tick to the delegate | ||
| guard count <= 2 else { | ||
| return | ||
| //do nothing | ||
| } | ||
| // when a tick happens, do this: | ||
| self.delegate?.didTick(2 - count) | ||
| }, onCompleted: { [unowned self] in | ||
| // when the timer completes, do this: | ||
| self.delegate?.didComplete() | ||
| }) | ||
| }.addDisposableTo(self.disposeBag) | ||
|
|
||
| } | ||
|
|
||
| } | ||
117 changes: 117 additions & 0 deletions
117
functional-reactive-intuition/Project/RFP/IntegratedReactiveViewController.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| // | ||
| // ViewController.swift | ||
| // RFP | ||
| // | ||
| // Created by Mark Aron Szulyovszky on 11/01/2016. | ||
| // Copyright © 2016 Mark Aron Szulyovszky. All rights reserved. | ||
| // | ||
|
|
||
| import UIKit | ||
| import RxSwift | ||
| import RxCocoa | ||
|
|
||
| class IntegratedReactiveViewController: UIViewController, SetStatus, GestureReactorDelegate { | ||
|
|
||
| @IBOutlet weak var draggableView: UIView! | ||
| @IBOutlet weak var statusLabel: UILabel! | ||
| @IBOutlet weak var centerXConstraint: NSLayoutConstraint! //For updating the position of the box when dragging | ||
| @IBOutlet weak var centerYConstraint: NSLayoutConstraint! | ||
|
|
||
| private let pan: UIPanGestureRecognizer | ||
| private let rotate: UIRotationGestureRecognizer | ||
| private var gestureReactor: IntegratedReactiveGestureReactor | ||
|
|
||
| private let disposeBag = DisposeBag() | ||
|
|
||
| required init?(coder aDecoder: NSCoder) { | ||
| pan = UIPanGestureRecognizer() | ||
| rotate = UIRotationGestureRecognizer() | ||
|
|
||
| // workaround to convert ControlEvent<UIGestureRecognizer> to Observable<UIGestureRecognizerType> | ||
| let panObservable: Observable<UIGestureRecognizerType> = pan.rx_event.asObservable().flatMap { gesture -> Observable<UIGestureRecognizerType> in | ||
| return Observable.just(gesture as UIGestureRecognizerType) | ||
| } | ||
| let rotateObservable: Observable<UIGestureRecognizerType> = rotate.rx_event.asObservable().flatMap { gesture -> Observable<UIGestureRecognizerType> in | ||
| return Observable.just(gesture as UIGestureRecognizerType) | ||
| } | ||
|
|
||
| gestureReactor = IntegratedReactiveGestureReactor(timerCreator: { interval in ReactiveTimerFactory.reactiveTimer(interval: interval) }, panGestureObservable: panObservable, rotateGestureObservable: rotateObservable) | ||
|
|
||
| super.init(coder: aDecoder) | ||
| } | ||
|
|
||
| // TODO as we like to have non-optional and non-implicitly-unwrapped properties, we need to execute the setup code in both initializers - unfortunately we can not call instance helper functions here with the current version of swift | ||
| override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { | ||
| pan = UIPanGestureRecognizer() | ||
| rotate = UIRotationGestureRecognizer() | ||
|
|
||
| // workaround to convert ControlEvent<UIGestureRecognizer> to Observable<UIGestureRecognizerType> | ||
| let panObservable: Observable<UIGestureRecognizerType> = pan.rx_event.asObservable().flatMap { gesture -> Observable<UIGestureRecognizerType> in | ||
| return Observable.just(gesture as UIGestureRecognizerType) | ||
| } | ||
| let rotateObservable: Observable<UIGestureRecognizerType> = rotate.rx_event.asObservable().flatMap { gesture -> Observable<UIGestureRecognizerType> in | ||
| return Observable.just(gesture as UIGestureRecognizerType) | ||
| } | ||
|
|
||
| gestureReactor = IntegratedReactiveGestureReactor(timerCreator: { interval in ReactiveTimerFactory.reactiveTimer(interval: interval) }, panGestureObservable: panObservable, rotateGestureObservable: rotateObservable) | ||
|
|
||
| super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) | ||
| } | ||
|
|
||
| override func viewDidLoad() { | ||
| super.viewDidLoad() | ||
|
|
||
| gestureReactor.delegate = self | ||
|
|
||
| self.draggableView.gestureRecognizers = [pan, rotate] | ||
|
|
||
|
|
||
| /// | ||
| /// | ||
| /// Extra Code to manipulate move and rotate the subview. | ||
| /// | ||
| /// Uses custom infix on CGPoint to '-' or '+' two together. | ||
|
|
||
| let panLocation = pan.rx_event.map { [unowned self] in | ||
| $0.locationInView(self.view) - self.view.center | ||
| } | ||
| panLocation.map { $0.x } | ||
| .bindTo(self.centerXConstraint.rx_constant) | ||
| .addDisposableTo(self.disposeBag) | ||
|
|
||
| panLocation.map { $0.y } | ||
| .bindTo(self.centerYConstraint.rx_constant) | ||
| .addDisposableTo(self.disposeBag) | ||
|
|
||
| rotate.rx_event | ||
| .map { ($0 as! UIRotationGestureRecognizer).rotation } | ||
| .bindTo(self.draggableView.rx_rotate) | ||
| .addDisposableTo(self.disposeBag) | ||
| } | ||
|
|
||
| override func viewWillAppear(animated: Bool) { | ||
| super.viewWillAppear(animated) | ||
| self.setStatus("Status: Waiting for Rotate & Pan") | ||
| } | ||
|
|
||
| func didStart() { | ||
| self.setStatus("Started") | ||
| } | ||
|
|
||
| func didTick(secondsLeft: Int) { | ||
| self.setStatus("Tick: \(secondsLeft)") | ||
| } | ||
|
|
||
| func didComplete() { | ||
| self.setStatus("Completed") | ||
| } | ||
|
|
||
| } | ||
|
|
||
| extension IntegratedReactiveViewController: UIGestureRecognizerDelegate { | ||
|
|
||
| func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool { | ||
| return true | ||
| } | ||
|
|
||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
somehow this is never called - strange, in
ReactiveGestureReactorexactly the same implementation is used, and there this line is called just fineThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@itchingpixels @morgz have you any idea why this behaves like this? i really cannot explain/fix it.