├── .gitignore ├── .gitmodules ├── Cartfile ├── Cartfile.resolved ├── Example ├── .swiftlint.yml ├── Podfile ├── Podfile.lock ├── RxGesture-OSX │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift ├── RxGesture.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── RxGesture_iOS_Demo.xcscheme ├── RxGesture.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── RxGesture │ ├── AppDelegate.swift │ ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Package.swift ├── Pod ├── Assets │ ├── .gitkeep │ └── demo.gif └── Classes │ ├── .gitkeep │ ├── GenericRxGestureRecognizerDelegate.swift │ ├── GestureFactory.swift │ ├── GestureRecognizer+RxGesture.swift │ ├── OSX │ ├── NSClickGestureRecognizer+RxGesture.swift │ ├── NSGestureRecognizer+Rx.swift │ ├── NSMagnificationGestureRecognizer+RxGesture.swift │ ├── NSPanGestureRecognizer+RxGesture.swift │ ├── NSPressGestureRecognizer+RxGesture.swift │ └── NSRotationGestureRecognizer+RxGesture.swift │ ├── SharedTypes.swift │ ├── View+RxGesture.swift │ └── iOS │ ├── ForceTouchGestureRecognizer.swift │ ├── TouchDownGestureRecognizer.swift │ ├── TransformGestureRecognizers.swift │ ├── UIHoverGestureRecognizer+RxGesture.swift │ ├── UILongPressGestureRecognizer+RxGesture.swift │ ├── UIPanGestureRecognizer+RxGesture.swift │ ├── UIPinchGestureRecognizer+RxGesture.swift │ ├── UIRotationGestureRecognizer+RxGesture.swift │ ├── UIScreenEdgePanGestureRecognizer+RxGesture.swift │ ├── UISwipeGestureRecognizer+RxGesture.swift │ └── UITapGestureRecognizer+RxGesture.swift ├── README.md ├── RxGesture.podspec ├── RxGesture ├── .swiftlint.yml ├── RxGesture copy-Info.plist ├── RxGesture-iOS-Tests │ ├── Info.plist │ └── RxGestureTests.swift ├── RxGesture.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ ├── RxGesture-OSX.xcscheme │ │ └── RxGesture-iOS.xcscheme └── RxGesture │ ├── Info.plist │ └── RxGesture.h └── bin └── setup /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## SPM Generated 10 | *.xcodeproj 11 | *.resolved 12 | .swiftpm/ 13 | 14 | ## Various settings 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | 25 | ## Other 26 | *.xccheckout 27 | *.moved-aside 28 | *.xcuserstate 29 | *.xcscmblueprint 30 | 31 | ## Obj-C/Swift specific 32 | *.hmap 33 | *.ipa 34 | 35 | ## Playgrounds 36 | timeline.xctimeline 37 | playground.xcworkspace 38 | 39 | # Swift Package Manager 40 | # 41 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 42 | # Packages/ 43 | .build/ 44 | 45 | # CocoaPods 46 | # 47 | # We recommend against adding the Pods directory to your .gitignore. However 48 | # you should judge for yourself, the pros and cons are mentioned at: 49 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 50 | # 51 | # Pods/ 52 | !Example/*.xcodeproj 53 | 54 | # Carthage 55 | # 56 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 57 | # Carthage/Checkouts 58 | 59 | Carthage/Build 60 | 61 | # fastlane 62 | # 63 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 64 | # screenshots whenever they are needed. 65 | # For more information about the recommended setup visit: 66 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 67 | 68 | fastlane/report.xml 69 | fastlane/screenshots 70 | Example/Pods 71 | 72 | .DS_Store 73 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Carthage/Checkouts/RxSwift"] 2 | path = Carthage/Checkouts/RxSwift 3 | url = https://github.com/ReactiveX/RxSwift.git 4 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "ReactiveX/RxSwift" ~> 6.0 -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "ReactiveX/RxSwift" "6.1.0" 2 | -------------------------------------------------------------------------------- /Example/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | excluded: 2 | - Pods 3 | 4 | opt_in_rules: 5 | - overridden_super_call 6 | - private_outlet 7 | - prohibited_super_call 8 | - first_where 9 | - closure_spacing 10 | - unneeded_parentheses_in_closure_argument 11 | - redundant_nil_coalescing 12 | - pattern_matching_keywords 13 | - explicit_init 14 | - contains_over_first_not_nil 15 | - implicit_return 16 | 17 | disabled_rules: 18 | - line_length 19 | - trailing_whitespace 20 | - type_name 21 | - identifier_name 22 | - vertical_whitespace 23 | - trailing_newline 24 | - opening_brace 25 | - large_tuple 26 | - file_length 27 | - comma 28 | - colon 29 | - private_over_fileprivate 30 | - force_cast 31 | - force_try 32 | - function_parameter_count 33 | - statement_position 34 | - legacy_hashing 35 | - todo 36 | - operator_whitespace 37 | - type_body_length 38 | - function_body_length 39 | - cyclomatic_complexity -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://cdn.cocoapods.org/' 2 | use_frameworks! 3 | 4 | def commonDependencies 5 | pod 'RxGesture', :path => '../' 6 | pod 'RxSwift', '~> 6.0' 7 | pod 'RxCocoa', '~> 6.0' 8 | end 9 | 10 | target 'RxGesture_iOS_Demo' do 11 | platform :ios, '9.0' 12 | commonDependencies 13 | end 14 | 15 | target 'RxGesture_OSX_Demo' do 16 | platform :osx, '10.10' 17 | commonDependencies 18 | end 19 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - RxCocoa (6.0.0): 3 | - RxRelay (= 6.0.0) 4 | - RxSwift (= 6.0.0) 5 | - RxGesture (4.0.0): 6 | - RxCocoa (~> 6.0) 7 | - RxSwift (~> 6.0) 8 | - RxRelay (6.0.0): 9 | - RxSwift (= 6.0.0) 10 | - RxSwift (6.0.0) 11 | 12 | DEPENDENCIES: 13 | - RxCocoa (~> 6.0) 14 | - RxGesture (from `../`) 15 | - RxSwift (~> 6.0) 16 | 17 | SPEC REPOS: 18 | trunk: 19 | - RxCocoa 20 | - RxRelay 21 | - RxSwift 22 | 23 | EXTERNAL SOURCES: 24 | RxGesture: 25 | :path: "../" 26 | 27 | SPEC CHECKSUMS: 28 | RxCocoa: 3f79328fafa3645b34600f37c31e64c73ae3a80e 29 | RxGesture: b095a86df33d199a33b46a644e7c58bdef8ab035 30 | RxRelay: 8d593be109c06ea850df027351beba614b012ffb 31 | RxSwift: c14e798c59b9f6e9a2df8fd235602e85cc044295 32 | 33 | PODFILE CHECKSUM: d48d39de6335e342c666df2ee12e4efb3d005eb3 34 | 35 | COCOAPODS: 1.10.1 36 | -------------------------------------------------------------------------------- /Example/RxGesture-OSX/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RxGesture-OSX 4 | // 5 | // Created by Marin Todorov on 3/24/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Example/RxGesture-OSX/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Example/RxGesture-OSX/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2016 CocoaPods. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /Example/RxGesture-OSX/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // RxGesture-OSX 4 | // 5 | // Created by Marin Todorov on 3/24/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | import RxSwift 12 | import RxCocoa 13 | import RxGesture 14 | 15 | class Step { 16 | enum Action { case previous, next } 17 | 18 | let title: String 19 | let code: String 20 | let install: (NSView, NSTextField, @escaping () -> Void, DisposeBag) -> Void 21 | 22 | init(title: String, code: String, install: @escaping (NSView, NSTextField, @escaping () -> Void, DisposeBag) -> Void) { 23 | self.title = title 24 | self.code = code 25 | self.install = install 26 | } 27 | } 28 | 29 | class MacViewController: NSViewController { 30 | 31 | @IBOutlet private weak var myView: NSView! 32 | @IBOutlet private weak var myViewText: NSTextField! 33 | @IBOutlet private weak var info: NSTextField! 34 | @IBOutlet private var code: NSTextView! 35 | 36 | fileprivate let nextStepObserver = PublishSubject() 37 | fileprivate let bag = DisposeBag() 38 | fileprivate var stepBag = DisposeBag() 39 | 40 | override func viewWillAppear() { 41 | super.viewWillAppear() 42 | 43 | view.wantsLayer = true 44 | 45 | myView.wantsLayer = true 46 | myView.layer?.backgroundColor = NSColor.red.cgColor 47 | myView.layer?.cornerRadius = 5 48 | 49 | let steps: [Step] = [ 50 | clickStep, 51 | doubleClickStep, 52 | rightClickStep, 53 | anyClickStep, 54 | pressStep, 55 | panStep, 56 | rotateStep, 57 | magnificationStep 58 | ] 59 | 60 | func newIndex(for index: Int, action: Step.Action) -> Int { 61 | switch action { 62 | case .previous: 63 | return (steps.count + index - 1) % steps.count 64 | case .next: 65 | return (steps.count + index + 1) % steps.count 66 | } 67 | } 68 | 69 | nextStepObserver 70 | .scan(0, accumulator: newIndex) 71 | .startWith(0) 72 | .map { (steps[$0], $0) } 73 | .subscribe(onNext: { [unowned self] in self.updateStep($0, at: $1) }) 74 | .disposed(by: bag) 75 | } 76 | 77 | override func viewDidAppear() { 78 | super.viewDidAppear() 79 | updateAnchorPoint() 80 | } 81 | 82 | private func updateAnchorPoint() { 83 | let frame = myView.layer!.frame 84 | myView.layer!.anchorPoint = CGPoint(x: 0.5, y: 0.5) 85 | myView.layer!.frame = frame 86 | } 87 | 88 | @IBAction func previousStep(_ sender: Any) { 89 | nextStepObserver.onNext(.previous) 90 | } 91 | 92 | @IBAction func nextStep(_ sender: Any) { 93 | nextStepObserver.onNext(.next) 94 | } 95 | 96 | func updateStep(_ step: Step, at index: Int) { 97 | stepBag = DisposeBag() 98 | 99 | info.stringValue = "\(index + 1). " + step.title 100 | code.string = step.code 101 | 102 | myViewText.stringValue = "" 103 | step.install(myView, myViewText, { [nextStepObserver] in nextStepObserver.onNext(.next) }, stepBag) 104 | 105 | print("active gestures: \(myView.gestureRecognizers.count)") 106 | } 107 | 108 | lazy var clickStep: Step = Step( 109 | title: "Click the square", 110 | code: """ 111 | view.rx 112 | .leftClickGesture() 113 | .when(.recognized) 114 | .subscribe(onNext: { _ in 115 | // Do something 116 | }) 117 | .disposed(by: disposeBag) 118 | """, 119 | install: { view, _, nextStep, stepBag in 120 | 121 | view.animateTransform(to: CATransform3DIdentity) 122 | view.animateBackgroundColor(to: .red) 123 | 124 | view.rx 125 | .leftClickGesture() 126 | .when(.recognized) 127 | .subscribe(onNext: { _ in 128 | nextStep() 129 | }) 130 | .disposed(by: stepBag) 131 | }) 132 | 133 | lazy var doubleClickStep: Step = Step( 134 | title: "Double click the square", 135 | code: """ 136 | view.rx 137 | .leftClickGesture { gesture, _ in 138 | gesture.numberOfClicksRequired = 2 139 | } 140 | .when(.recognized) 141 | .subscribe(onNext: { _ in 142 | // Do something 143 | }) 144 | .disposed(by: disposeBag) 145 | """, 146 | install: { view, _, nextStep, stepBag in 147 | 148 | view.animateTransform(to: CATransform3DIdentity) 149 | view.animateBackgroundColor(to: .green) 150 | 151 | view.rx 152 | .leftClickGesture { gesture, _ in 153 | gesture.numberOfClicksRequired = 2 154 | } 155 | .when(.recognized) 156 | .subscribe(onNext: { _ in 157 | nextStep() 158 | }) 159 | .disposed(by: stepBag) 160 | }) 161 | 162 | lazy var rightClickStep: Step = Step( 163 | title: "Right click the square", 164 | code: """ 165 | view.rx 166 | .rightClickGesture() 167 | .when(.recognized) 168 | .subscribe(onNext: { _ in 169 | // Do something 170 | }) 171 | .disposed(by: disposeBag) 172 | """, 173 | install: { view, _, nextStep, stepBag in 174 | 175 | view.animateTransform(to: CATransform3DIdentity) 176 | view.animateBackgroundColor(to: .blue) 177 | 178 | view.rx 179 | .rightClickGesture() 180 | .when(.recognized) 181 | .subscribe(onNext: { _ in 182 | nextStep() 183 | }) 184 | .disposed(by: stepBag) 185 | }) 186 | 187 | lazy var anyClickStep: Step = Step( 188 | title: "Click any button (left or right)", 189 | code: """ 190 | view.rx 191 | .anyGesture(.leftClick(), .rightClick()) 192 | .when(.recognized) 193 | .subscribe(onNext: { _ in 194 | // Do something 195 | }) 196 | .disposed(by: disposeBag) 197 | """, 198 | install: { view, _, nextStep, stepBag in 199 | 200 | view.animateTransform(to: CATransform3DMakeScale(1.5, 1.5, 1.0)) 201 | view.animateBackgroundColor(to: .red) 202 | 203 | view.rx 204 | .anyGesture(.leftClick(), .rightClick()) 205 | .when(.recognized) 206 | .subscribe(onNext: { _ in 207 | nextStep() 208 | }) 209 | .disposed(by: stepBag) 210 | }) 211 | 212 | lazy var pressStep: Step = Step( 213 | title: "Long press the square", 214 | code: """ 215 | view.rx 216 | .pressGesture() 217 | .when(.began) 218 | .subscribe(onNext: { _ in 219 | // Do something 220 | }) 221 | .disposed(by: disposeBag) 222 | """, 223 | install: { view, _, nextStep, stepBag in 224 | 225 | view.animateBackgroundColor(to: .red) 226 | view.animateTransform(to: CATransform3DMakeScale(2.0, 2.0, 1.0)) 227 | 228 | view.rx 229 | .pressGesture() 230 | .when(.began) 231 | .subscribe(onNext: { _ in 232 | nextStep() 233 | }) 234 | .disposed(by: stepBag) 235 | }) 236 | 237 | lazy var panStep: Step = Step( 238 | title: "Drag the square around", 239 | code: """ 240 | view.rx 241 | .panGesture() 242 | .when(.changed) 243 | .asTranslation() 244 | .subscribe(onNext: { _ in 245 | // Do something 246 | }) 247 | .disposed(by: disposeBag) 248 | 249 | view.rx 250 | .anyGesture( 251 | (.pan(), when: .ended), 252 | (.click(), when: .recognized) 253 | ) 254 | .subscribe(onNext: { _ in 255 | // Do something 256 | }) 257 | .disposed(by: disposeBag) 258 | """, 259 | install: { view, label, nextStep, stepBag in 260 | 261 | view.animateTransform(to: CATransform3DIdentity) 262 | 263 | view.rx 264 | .panGesture() 265 | .when(.changed) 266 | .asTranslation() 267 | .subscribe(onNext: { translation, _ in 268 | label.stringValue = String(format: "(%.f, %.f)", arguments: [translation.x, translation.y]) 269 | view.layer!.transform = CATransform3DMakeTranslation(translation.x, translation.y, 0.0) 270 | }) 271 | .disposed(by: stepBag) 272 | 273 | view.rx 274 | .anyGesture( 275 | (.pan(), when: .ended), 276 | (.leftClick(), when: .recognized) 277 | ) 278 | .subscribe(onNext: { _ in 279 | nextStep() 280 | }) 281 | .disposed(by: stepBag) 282 | }) 283 | 284 | lazy var rotateStep: Step = Step( 285 | title: "Rotate the square with your trackpad, or click if you do not have a trackpad", 286 | code: """ 287 | view.rx 288 | .rotationGesture() 289 | .when(.changed) 290 | .asRotation() 291 | .subscribe(onNext: { _ in 292 | // Do something 293 | }) 294 | .disposed(by: disposeBag) 295 | 296 | view.rx 297 | .anyGesture( 298 | (.rotation(), when: .ended), 299 | (.click(), when: .recognized) 300 | ) 301 | .subscribe(onNext: { _ in 302 | // Do something 303 | }) 304 | .disposed(by: disposeBag) 305 | """, 306 | install: { view, label, nextStep, stepBag in 307 | 308 | view.animateTransform(to: CATransform3DIdentity) 309 | 310 | view.rx 311 | .rotationGesture() 312 | .when(.changed) 313 | .asRotation() 314 | .subscribe(onNext: { rotation in 315 | label.stringValue = String(format: "%.0fº", rotation * 180 / .pi) 316 | view.layer!.transform = CATransform3DMakeRotation(rotation, 0, 0, 1) 317 | }) 318 | .disposed(by: stepBag) 319 | 320 | view.rx 321 | .anyGesture( 322 | (.rotation(), when: .ended), 323 | (.leftClick(), when: .recognized) 324 | ) 325 | .subscribe(onNext: { _ in 326 | nextStep() 327 | }) 328 | .disposed(by: stepBag) 329 | }) 330 | 331 | lazy var magnificationStep: Step = Step( 332 | title: "Pinch the square with your trackpad, or click if you do not have a trackpad", 333 | code: """ 334 | view.rx 335 | .magnificationGesture() 336 | .when(.changed) 337 | .asScale() 338 | .subscribe(onNext: { _ in 339 | // Do something 340 | }) 341 | .disposed(by: disposeBag) 342 | 343 | view.rx 344 | .anyGesture( 345 | (.magnification(), when: .ended), 346 | (.click(), when: .recognized) 347 | ) 348 | .subscribe(onNext: { _ in 349 | // Do something 350 | }) 351 | .disposed(by: disposeBag) 352 | """, 353 | install: { view, label, nextStep, stepBag in 354 | 355 | view.animateTransform(to: CATransform3DIdentity) 356 | 357 | view.rx 358 | .magnificationGesture() 359 | .when(.changed) 360 | .asScale() 361 | .subscribe(onNext: { scale in 362 | label.stringValue = String(format: "scale: %.2f", scale) 363 | view.layer!.transform = CATransform3DMakeScale(scale, scale, 1) 364 | }) 365 | .disposed(by: stepBag) 366 | 367 | view.rx 368 | .anyGesture( 369 | (.magnification(), when: .ended), 370 | (.leftClick(), when: .recognized) 371 | ) 372 | .subscribe(onNext: { _ in 373 | nextStep() 374 | }) 375 | .disposed(by: stepBag) 376 | }) 377 | 378 | } 379 | 380 | private extension NSView { 381 | 382 | func animateTransform(to transform: CATransform3D) { 383 | let initialTransform = self.layer?.presentation()?.transform ?? self.layer!.model().transform 384 | 385 | let anim = CABasicAnimation(keyPath: "transform") 386 | anim.duration = 0.5 387 | anim.fromValue = NSValue(caTransform3D: initialTransform) 388 | anim.toValue = NSValue(caTransform3D: transform) 389 | self.layer!.add(anim, forKey: nil) 390 | self.layer!.transform = transform 391 | } 392 | 393 | func animateBackgroundColor(to color: NSColor) { 394 | let initialColor = self.layer?.presentation()?.backgroundColor ?? self.layer?.model().backgroundColor 395 | 396 | let anim = CABasicAnimation(keyPath: "backgroundColor") 397 | anim.duration = 0.5 398 | anim.fromValue = initialColor 399 | anim.toValue = color.cgColor 400 | self.layer!.add(anim, forKey: nil) 401 | self.layer!.backgroundColor = color.cgColor 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /Example/RxGesture.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5D528E505C4F09640FE69137 /* Pods_RxGesture_iOS_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8E0FE7FD951353CF0314F2E /* Pods_RxGesture_iOS_Demo.framework */; }; 11 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 12 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 13 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 14 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 15 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 16 | 93A57689D62FEAC950BE2B05 /* Pods_RxGesture_OSX_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A0DE1183287EB1C1B4A27A6 /* Pods_RxGesture_OSX_Demo.framework */; }; 17 | 9CBB5C931CA3D9C30089E3EA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBB5C921CA3D9C30089E3EA /* AppDelegate.swift */; }; 18 | 9CBB5C951CA3D9C30089E3EA /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBB5C941CA3D9C30089E3EA /* ViewController.swift */; }; 19 | 9CBB5C971CA3D9C30089E3EA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9CBB5C961CA3D9C30089E3EA /* Assets.xcassets */; }; 20 | 9CBB5C9A1CA3D9C30089E3EA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9CBB5C981CA3D9C30089E3EA /* Main.storyboard */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 2318903E5096B0D250136D79 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 25 | 47A5D1CA9387C23F09632B82 /* Pods-RxGesture_iOS_Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxGesture_iOS_Demo.release.xcconfig"; path = "Pods/Target Support Files/Pods-RxGesture_iOS_Demo/Pods-RxGesture_iOS_Demo.release.xcconfig"; sourceTree = ""; }; 26 | 607FACD01AFB9204008FA782 /* RxGesture_iOS_Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxGesture_iOS_Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 28 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 29 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 30 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 31 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 32 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 33 | 63BA1167EB70331BB5507B2C /* Pods-RxGesture_OSX_Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxGesture_OSX_Demo.release.xcconfig"; path = "Pods/Target Support Files/Pods-RxGesture_OSX_Demo/Pods-RxGesture_OSX_Demo.release.xcconfig"; sourceTree = ""; }; 34 | 6C7E30D81C8E863C3A3EF411 /* Pods-RxGesture_OSX_Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxGesture_OSX_Demo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RxGesture_OSX_Demo/Pods-RxGesture_OSX_Demo.debug.xcconfig"; sourceTree = ""; }; 35 | 8A0DE1183287EB1C1B4A27A6 /* Pods_RxGesture_OSX_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RxGesture_OSX_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 9C94D1DF1CB3F9AC00F94AEE /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = "Pods/../build/Debug/Pods-RxGesture_OSX_Demo/RxCocoa.framework"; sourceTree = ""; }; 37 | 9C94D1E01CB3F9AC00F94AEE /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = "Pods/../build/Debug/Pods-RxGesture_OSX_Demo/RxSwift.framework"; sourceTree = ""; }; 38 | 9CBB5C901CA3D9C30089E3EA /* RxGesture_OSX_Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxGesture_OSX_Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 9CBB5C921CA3D9C30089E3EA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 40 | 9CBB5C941CA3D9C30089E3EA /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 41 | 9CBB5C961CA3D9C30089E3EA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 42 | 9CBB5C991CA3D9C30089E3EA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 43 | 9CBB5C9B1CA3D9C30089E3EA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | A32D46FFF817D17B80E368BB /* RxGesture.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = RxGesture.podspec; path = ../RxGesture.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 45 | A8ACB04E64DDA0D81C1AD26C /* Pods-RxGesture_iOS_Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxGesture_iOS_Demo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RxGesture_iOS_Demo/Pods-RxGesture_iOS_Demo.debug.xcconfig"; sourceTree = ""; }; 46 | AEA5E5F8794395306DA818D0 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 47 | F8E0FE7FD951353CF0314F2E /* Pods_RxGesture_iOS_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RxGesture_iOS_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | /* End PBXFileReference section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | 5D528E505C4F09640FE69137 /* Pods_RxGesture_iOS_Demo.framework in Frameworks */, 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | 9CBB5C8D1CA3D9C30089E3EA /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | 93A57689D62FEAC950BE2B05 /* Pods_RxGesture_OSX_Demo.framework in Frameworks */, 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | /* End PBXFrameworksBuildPhase section */ 68 | 69 | /* Begin PBXGroup section */ 70 | 20A9EE12C51FB5E7E9F4C7D5 /* Pods */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | 6C7E30D81C8E863C3A3EF411 /* Pods-RxGesture_OSX_Demo.debug.xcconfig */, 74 | 63BA1167EB70331BB5507B2C /* Pods-RxGesture_OSX_Demo.release.xcconfig */, 75 | A8ACB04E64DDA0D81C1AD26C /* Pods-RxGesture_iOS_Demo.debug.xcconfig */, 76 | 47A5D1CA9387C23F09632B82 /* Pods-RxGesture_iOS_Demo.release.xcconfig */, 77 | ); 78 | name = Pods; 79 | sourceTree = ""; 80 | }; 81 | 607FACC71AFB9204008FA782 = { 82 | isa = PBXGroup; 83 | children = ( 84 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 85 | 607FACD21AFB9204008FA782 /* RxGesture iOS Example */, 86 | 9CBB5C911CA3D9C30089E3EA /* RxGesture OSX Example */, 87 | 607FACD11AFB9204008FA782 /* Products */, 88 | DBB6C45CE5FC3994913798D8 /* Frameworks */, 89 | 20A9EE12C51FB5E7E9F4C7D5 /* Pods */, 90 | ); 91 | sourceTree = ""; 92 | }; 93 | 607FACD11AFB9204008FA782 /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 607FACD01AFB9204008FA782 /* RxGesture_iOS_Demo.app */, 97 | 9CBB5C901CA3D9C30089E3EA /* RxGesture_OSX_Demo.app */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 607FACD21AFB9204008FA782 /* RxGesture iOS Example */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 106 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 107 | 607FACD91AFB9204008FA782 /* Main.storyboard */, 108 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 109 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 110 | 607FACD31AFB9204008FA782 /* Supporting Files */, 111 | ); 112 | name = "RxGesture iOS Example "; 113 | path = RxGesture; 114 | sourceTree = ""; 115 | }; 116 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 607FACD41AFB9204008FA782 /* Info.plist */, 120 | ); 121 | name = "Supporting Files"; 122 | sourceTree = ""; 123 | }; 124 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | A32D46FFF817D17B80E368BB /* RxGesture.podspec */, 128 | AEA5E5F8794395306DA818D0 /* README.md */, 129 | 2318903E5096B0D250136D79 /* LICENSE */, 130 | ); 131 | name = "Podspec Metadata"; 132 | sourceTree = ""; 133 | }; 134 | 9CBB5C911CA3D9C30089E3EA /* RxGesture OSX Example */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 9CBB5C921CA3D9C30089E3EA /* AppDelegate.swift */, 138 | 9CBB5C941CA3D9C30089E3EA /* ViewController.swift */, 139 | 9CBB5C961CA3D9C30089E3EA /* Assets.xcassets */, 140 | 9CBB5C981CA3D9C30089E3EA /* Main.storyboard */, 141 | 9CBB5C9B1CA3D9C30089E3EA /* Info.plist */, 142 | ); 143 | name = "RxGesture OSX Example"; 144 | path = "RxGesture-OSX"; 145 | sourceTree = ""; 146 | }; 147 | DBB6C45CE5FC3994913798D8 /* Frameworks */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 9C94D1DF1CB3F9AC00F94AEE /* RxCocoa.framework */, 151 | 9C94D1E01CB3F9AC00F94AEE /* RxSwift.framework */, 152 | 8A0DE1183287EB1C1B4A27A6 /* Pods_RxGesture_OSX_Demo.framework */, 153 | F8E0FE7FD951353CF0314F2E /* Pods_RxGesture_iOS_Demo.framework */, 154 | ); 155 | name = Frameworks; 156 | sourceTree = ""; 157 | }; 158 | /* End PBXGroup section */ 159 | 160 | /* Begin PBXNativeTarget section */ 161 | 607FACCF1AFB9204008FA782 /* RxGesture_iOS_Demo */ = { 162 | isa = PBXNativeTarget; 163 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "RxGesture_iOS_Demo" */; 164 | buildPhases = ( 165 | CDB795DBD98169E1348CB0E6 /* [CP] Check Pods Manifest.lock */, 166 | 786B9ECF22A850FF005D335F /* Swiftlint */, 167 | 607FACCC1AFB9204008FA782 /* Sources */, 168 | 607FACCD1AFB9204008FA782 /* Frameworks */, 169 | 607FACCE1AFB9204008FA782 /* Resources */, 170 | 58D78C3A772A244F05D5E0CC /* [CP] Embed Pods Frameworks */, 171 | ); 172 | buildRules = ( 173 | ); 174 | dependencies = ( 175 | ); 176 | name = RxGesture_iOS_Demo; 177 | productName = RxGesture; 178 | productReference = 607FACD01AFB9204008FA782 /* RxGesture_iOS_Demo.app */; 179 | productType = "com.apple.product-type.application"; 180 | }; 181 | 9CBB5C8F1CA3D9C30089E3EA /* RxGesture_OSX_Demo */ = { 182 | isa = PBXNativeTarget; 183 | buildConfigurationList = 9CBB5C9E1CA3D9C30089E3EA /* Build configuration list for PBXNativeTarget "RxGesture_OSX_Demo" */; 184 | buildPhases = ( 185 | 4884FD709FE61C738647B2EE /* [CP] Check Pods Manifest.lock */, 186 | 9CBB5C8C1CA3D9C30089E3EA /* Sources */, 187 | 9CBB5C8D1CA3D9C30089E3EA /* Frameworks */, 188 | 9CBB5C8E1CA3D9C30089E3EA /* Resources */, 189 | 806AE04460EE07E485B38C35 /* [CP] Embed Pods Frameworks */, 190 | ); 191 | buildRules = ( 192 | ); 193 | dependencies = ( 194 | ); 195 | name = RxGesture_OSX_Demo; 196 | productName = "RxGesture-OSX"; 197 | productReference = 9CBB5C901CA3D9C30089E3EA /* RxGesture_OSX_Demo.app */; 198 | productType = "com.apple.product-type.application"; 199 | }; 200 | /* End PBXNativeTarget section */ 201 | 202 | /* Begin PBXProject section */ 203 | 607FACC81AFB9204008FA782 /* Project object */ = { 204 | isa = PBXProject; 205 | attributes = { 206 | LastSwiftUpdateCheck = 0730; 207 | LastUpgradeCheck = 1000; 208 | ORGANIZATIONNAME = CocoaPods; 209 | TargetAttributes = { 210 | 607FACCF1AFB9204008FA782 = { 211 | CreatedOnToolsVersion = 6.3.1; 212 | DevelopmentTeam = JKFCB4CN7C; 213 | LastSwiftMigration = 1020; 214 | ProvisioningStyle = Manual; 215 | }; 216 | 9CBB5C8F1CA3D9C30089E3EA = { 217 | CreatedOnToolsVersion = 7.3; 218 | LastSwiftMigration = 0800; 219 | }; 220 | }; 221 | }; 222 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "RxGesture" */; 223 | compatibilityVersion = "Xcode 3.2"; 224 | developmentRegion = en; 225 | hasScannedForEncodings = 0; 226 | knownRegions = ( 227 | en, 228 | Base, 229 | ); 230 | mainGroup = 607FACC71AFB9204008FA782; 231 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 232 | projectDirPath = ""; 233 | projectRoot = ""; 234 | targets = ( 235 | 607FACCF1AFB9204008FA782 /* RxGesture_iOS_Demo */, 236 | 9CBB5C8F1CA3D9C30089E3EA /* RxGesture_OSX_Demo */, 237 | ); 238 | }; 239 | /* End PBXProject section */ 240 | 241 | /* Begin PBXResourcesBuildPhase section */ 242 | 607FACCE1AFB9204008FA782 /* Resources */ = { 243 | isa = PBXResourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 247 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 248 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | 9CBB5C8E1CA3D9C30089E3EA /* Resources */ = { 253 | isa = PBXResourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | 9CBB5C971CA3D9C30089E3EA /* Assets.xcassets in Resources */, 257 | 9CBB5C9A1CA3D9C30089E3EA /* Main.storyboard in Resources */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXResourcesBuildPhase section */ 262 | 263 | /* Begin PBXShellScriptBuildPhase section */ 264 | 4884FD709FE61C738647B2EE /* [CP] Check Pods Manifest.lock */ = { 265 | isa = PBXShellScriptBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ); 269 | inputPaths = ( 270 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 271 | "${PODS_ROOT}/Manifest.lock", 272 | ); 273 | name = "[CP] Check Pods Manifest.lock"; 274 | outputPaths = ( 275 | "$(DERIVED_FILE_DIR)/Pods-RxGesture_OSX_Demo-checkManifestLockResult.txt", 276 | ); 277 | runOnlyForDeploymentPostprocessing = 0; 278 | shellPath = /bin/sh; 279 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 280 | showEnvVarsInLog = 0; 281 | }; 282 | 58D78C3A772A244F05D5E0CC /* [CP] Embed Pods Frameworks */ = { 283 | isa = PBXShellScriptBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | ); 287 | inputPaths = ( 288 | "${PODS_ROOT}/Target Support Files/Pods-RxGesture_iOS_Demo/Pods-RxGesture_iOS_Demo-frameworks.sh", 289 | "${BUILT_PRODUCTS_DIR}/RxCocoa-iOS/RxCocoa.framework", 290 | "${BUILT_PRODUCTS_DIR}/RxGesture-iOS/RxGesture.framework", 291 | "${BUILT_PRODUCTS_DIR}/RxRelay-iOS/RxRelay.framework", 292 | "${BUILT_PRODUCTS_DIR}/RxSwift-iOS/RxSwift.framework", 293 | ); 294 | name = "[CP] Embed Pods Frameworks"; 295 | outputPaths = ( 296 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework", 297 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxGesture.framework", 298 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxRelay.framework", 299 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework", 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | shellPath = /bin/sh; 303 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RxGesture_iOS_Demo/Pods-RxGesture_iOS_Demo-frameworks.sh\"\n"; 304 | showEnvVarsInLog = 0; 305 | }; 306 | 786B9ECF22A850FF005D335F /* Swiftlint */ = { 307 | isa = PBXShellScriptBuildPhase; 308 | buildActionMask = 2147483647; 309 | files = ( 310 | ); 311 | inputFileListPaths = ( 312 | ); 313 | inputPaths = ( 314 | ); 315 | name = Swiftlint; 316 | outputFileListPaths = ( 317 | ); 318 | outputPaths = ( 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | shellPath = /bin/sh; 322 | shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; 323 | }; 324 | 806AE04460EE07E485B38C35 /* [CP] Embed Pods Frameworks */ = { 325 | isa = PBXShellScriptBuildPhase; 326 | buildActionMask = 2147483647; 327 | files = ( 328 | ); 329 | inputPaths = ( 330 | "${PODS_ROOT}/Target Support Files/Pods-RxGesture_OSX_Demo/Pods-RxGesture_OSX_Demo-frameworks.sh", 331 | "${BUILT_PRODUCTS_DIR}/RxCocoa-macOS/RxCocoa.framework", 332 | "${BUILT_PRODUCTS_DIR}/RxGesture-macOS/RxGesture.framework", 333 | "${BUILT_PRODUCTS_DIR}/RxRelay-macOS/RxRelay.framework", 334 | "${BUILT_PRODUCTS_DIR}/RxSwift-macOS/RxSwift.framework", 335 | ); 336 | name = "[CP] Embed Pods Frameworks"; 337 | outputPaths = ( 338 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework", 339 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxGesture.framework", 340 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxRelay.framework", 341 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework", 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | shellPath = /bin/sh; 345 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RxGesture_OSX_Demo/Pods-RxGesture_OSX_Demo-frameworks.sh\"\n"; 346 | showEnvVarsInLog = 0; 347 | }; 348 | CDB795DBD98169E1348CB0E6 /* [CP] Check Pods Manifest.lock */ = { 349 | isa = PBXShellScriptBuildPhase; 350 | buildActionMask = 2147483647; 351 | files = ( 352 | ); 353 | inputPaths = ( 354 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 355 | "${PODS_ROOT}/Manifest.lock", 356 | ); 357 | name = "[CP] Check Pods Manifest.lock"; 358 | outputPaths = ( 359 | "$(DERIVED_FILE_DIR)/Pods-RxGesture_iOS_Demo-checkManifestLockResult.txt", 360 | ); 361 | runOnlyForDeploymentPostprocessing = 0; 362 | shellPath = /bin/sh; 363 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 364 | showEnvVarsInLog = 0; 365 | }; 366 | /* End PBXShellScriptBuildPhase section */ 367 | 368 | /* Begin PBXSourcesBuildPhase section */ 369 | 607FACCC1AFB9204008FA782 /* Sources */ = { 370 | isa = PBXSourcesBuildPhase; 371 | buildActionMask = 2147483647; 372 | files = ( 373 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 374 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 375 | ); 376 | runOnlyForDeploymentPostprocessing = 0; 377 | }; 378 | 9CBB5C8C1CA3D9C30089E3EA /* Sources */ = { 379 | isa = PBXSourcesBuildPhase; 380 | buildActionMask = 2147483647; 381 | files = ( 382 | 9CBB5C951CA3D9C30089E3EA /* ViewController.swift in Sources */, 383 | 9CBB5C931CA3D9C30089E3EA /* AppDelegate.swift in Sources */, 384 | ); 385 | runOnlyForDeploymentPostprocessing = 0; 386 | }; 387 | /* End PBXSourcesBuildPhase section */ 388 | 389 | /* Begin PBXVariantGroup section */ 390 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = { 391 | isa = PBXVariantGroup; 392 | children = ( 393 | 607FACDA1AFB9204008FA782 /* Base */, 394 | ); 395 | name = Main.storyboard; 396 | sourceTree = ""; 397 | }; 398 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 399 | isa = PBXVariantGroup; 400 | children = ( 401 | 607FACDF1AFB9204008FA782 /* Base */, 402 | ); 403 | name = LaunchScreen.xib; 404 | sourceTree = ""; 405 | }; 406 | 9CBB5C981CA3D9C30089E3EA /* Main.storyboard */ = { 407 | isa = PBXVariantGroup; 408 | children = ( 409 | 9CBB5C991CA3D9C30089E3EA /* Base */, 410 | ); 411 | name = Main.storyboard; 412 | sourceTree = ""; 413 | }; 414 | /* End PBXVariantGroup section */ 415 | 416 | /* Begin XCBuildConfiguration section */ 417 | 607FACED1AFB9204008FA782 /* Debug */ = { 418 | isa = XCBuildConfiguration; 419 | buildSettings = { 420 | ALWAYS_SEARCH_USER_PATHS = NO; 421 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 422 | CLANG_CXX_LIBRARY = "libc++"; 423 | CLANG_ENABLE_MODULES = YES; 424 | CLANG_ENABLE_OBJC_ARC = YES; 425 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 426 | CLANG_WARN_BOOL_CONVERSION = YES; 427 | CLANG_WARN_COMMA = YES; 428 | CLANG_WARN_CONSTANT_CONVERSION = YES; 429 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 430 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 431 | CLANG_WARN_EMPTY_BODY = YES; 432 | CLANG_WARN_ENUM_CONVERSION = YES; 433 | CLANG_WARN_INFINITE_RECURSION = YES; 434 | CLANG_WARN_INT_CONVERSION = YES; 435 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 436 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 437 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 438 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 439 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 440 | CLANG_WARN_STRICT_PROTOTYPES = YES; 441 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 442 | CLANG_WARN_UNREACHABLE_CODE = YES; 443 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 444 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 445 | COPY_PHASE_STRIP = NO; 446 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 447 | ENABLE_STRICT_OBJC_MSGSEND = YES; 448 | ENABLE_TESTABILITY = YES; 449 | GCC_C_LANGUAGE_STANDARD = gnu99; 450 | GCC_DYNAMIC_NO_PIC = NO; 451 | GCC_NO_COMMON_BLOCKS = YES; 452 | GCC_OPTIMIZATION_LEVEL = 0; 453 | GCC_PREPROCESSOR_DEFINITIONS = ( 454 | "DEBUG=1", 455 | "$(inherited)", 456 | ); 457 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 458 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 459 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 460 | GCC_WARN_UNDECLARED_SELECTOR = YES; 461 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 462 | GCC_WARN_UNUSED_FUNCTION = YES; 463 | GCC_WARN_UNUSED_VARIABLE = YES; 464 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 465 | MTL_ENABLE_DEBUG_INFO = YES; 466 | ONLY_ACTIVE_ARCH = YES; 467 | SDKROOT = iphoneos; 468 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 469 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 470 | SWIFT_VERSION = 5.0; 471 | }; 472 | name = Debug; 473 | }; 474 | 607FACEE1AFB9204008FA782 /* Release */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | ALWAYS_SEARCH_USER_PATHS = NO; 478 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 479 | CLANG_CXX_LIBRARY = "libc++"; 480 | CLANG_ENABLE_MODULES = YES; 481 | CLANG_ENABLE_OBJC_ARC = YES; 482 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 483 | CLANG_WARN_BOOL_CONVERSION = YES; 484 | CLANG_WARN_COMMA = YES; 485 | CLANG_WARN_CONSTANT_CONVERSION = YES; 486 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 487 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 488 | CLANG_WARN_EMPTY_BODY = YES; 489 | CLANG_WARN_ENUM_CONVERSION = YES; 490 | CLANG_WARN_INFINITE_RECURSION = YES; 491 | CLANG_WARN_INT_CONVERSION = YES; 492 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 493 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 494 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 495 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 496 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 497 | CLANG_WARN_STRICT_PROTOTYPES = YES; 498 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 499 | CLANG_WARN_UNREACHABLE_CODE = YES; 500 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 501 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 502 | COPY_PHASE_STRIP = NO; 503 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 504 | ENABLE_NS_ASSERTIONS = NO; 505 | ENABLE_STRICT_OBJC_MSGSEND = YES; 506 | GCC_C_LANGUAGE_STANDARD = gnu99; 507 | GCC_NO_COMMON_BLOCKS = YES; 508 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 509 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 510 | GCC_WARN_UNDECLARED_SELECTOR = YES; 511 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 512 | GCC_WARN_UNUSED_FUNCTION = YES; 513 | GCC_WARN_UNUSED_VARIABLE = YES; 514 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 515 | MTL_ENABLE_DEBUG_INFO = NO; 516 | SDKROOT = iphoneos; 517 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 518 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 519 | SWIFT_VERSION = 5.0; 520 | VALIDATE_PRODUCT = YES; 521 | }; 522 | name = Release; 523 | }; 524 | 607FACF01AFB9204008FA782 /* Debug */ = { 525 | isa = XCBuildConfiguration; 526 | baseConfigurationReference = A8ACB04E64DDA0D81C1AD26C /* Pods-RxGesture_iOS_Demo.debug.xcconfig */; 527 | buildSettings = { 528 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; 529 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 530 | CODE_SIGN_STYLE = Manual; 531 | DEVELOPMENT_TEAM = JKFCB4CN7C; 532 | INFOPLIST_FILE = RxGesture/Info.plist; 533 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 534 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 535 | MODULE_NAME = ExampleApp; 536 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 537 | PRODUCT_NAME = "$(TARGET_NAME)"; 538 | PROVISIONING_PROFILE_SPECIFIER = "Wildcard Development"; 539 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 540 | SWIFT_VERSION = 5.0; 541 | }; 542 | name = Debug; 543 | }; 544 | 607FACF11AFB9204008FA782 /* Release */ = { 545 | isa = XCBuildConfiguration; 546 | baseConfigurationReference = 47A5D1CA9387C23F09632B82 /* Pods-RxGesture_iOS_Demo.release.xcconfig */; 547 | buildSettings = { 548 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; 549 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 550 | CODE_SIGN_STYLE = Manual; 551 | DEVELOPMENT_TEAM = JKFCB4CN7C; 552 | INFOPLIST_FILE = RxGesture/Info.plist; 553 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 554 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 555 | MODULE_NAME = ExampleApp; 556 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 557 | PRODUCT_NAME = "$(TARGET_NAME)"; 558 | PROVISIONING_PROFILE_SPECIFIER = "Wildcard Development"; 559 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 560 | SWIFT_VERSION = 5.0; 561 | }; 562 | name = Release; 563 | }; 564 | 9CBB5C9C1CA3D9C30089E3EA /* Debug */ = { 565 | isa = XCBuildConfiguration; 566 | baseConfigurationReference = 6C7E30D81C8E863C3A3EF411 /* Pods-RxGesture_OSX_Demo.debug.xcconfig */; 567 | buildSettings = { 568 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; 569 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 570 | CLANG_ANALYZER_NONNULL = YES; 571 | CODE_SIGN_IDENTITY = "-"; 572 | COMBINE_HIDPI_IMAGES = YES; 573 | DEBUG_INFORMATION_FORMAT = dwarf; 574 | INFOPLIST_FILE = "RxGesture-OSX/Info.plist"; 575 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 576 | MACOSX_DEPLOYMENT_TARGET = 10.10; 577 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.RxGesture-OSX-Demo"; 578 | PRODUCT_NAME = "$(TARGET_NAME)"; 579 | SDKROOT = macosx; 580 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 581 | SWIFT_VERSION = 5.0; 582 | }; 583 | name = Debug; 584 | }; 585 | 9CBB5C9D1CA3D9C30089E3EA /* Release */ = { 586 | isa = XCBuildConfiguration; 587 | baseConfigurationReference = 63BA1167EB70331BB5507B2C /* Pods-RxGesture_OSX_Demo.release.xcconfig */; 588 | buildSettings = { 589 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; 590 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 591 | CLANG_ANALYZER_NONNULL = YES; 592 | CODE_SIGN_IDENTITY = "-"; 593 | COMBINE_HIDPI_IMAGES = YES; 594 | INFOPLIST_FILE = "RxGesture-OSX/Info.plist"; 595 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 596 | MACOSX_DEPLOYMENT_TARGET = 10.10; 597 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.RxGesture-OSX-Demo"; 598 | PRODUCT_NAME = "$(TARGET_NAME)"; 599 | SDKROOT = macosx; 600 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 601 | SWIFT_VERSION = 5.0; 602 | }; 603 | name = Release; 604 | }; 605 | /* End XCBuildConfiguration section */ 606 | 607 | /* Begin XCConfigurationList section */ 608 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "RxGesture" */ = { 609 | isa = XCConfigurationList; 610 | buildConfigurations = ( 611 | 607FACED1AFB9204008FA782 /* Debug */, 612 | 607FACEE1AFB9204008FA782 /* Release */, 613 | ); 614 | defaultConfigurationIsVisible = 0; 615 | defaultConfigurationName = Release; 616 | }; 617 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "RxGesture_iOS_Demo" */ = { 618 | isa = XCConfigurationList; 619 | buildConfigurations = ( 620 | 607FACF01AFB9204008FA782 /* Debug */, 621 | 607FACF11AFB9204008FA782 /* Release */, 622 | ); 623 | defaultConfigurationIsVisible = 0; 624 | defaultConfigurationName = Release; 625 | }; 626 | 9CBB5C9E1CA3D9C30089E3EA /* Build configuration list for PBXNativeTarget "RxGesture_OSX_Demo" */ = { 627 | isa = XCConfigurationList; 628 | buildConfigurations = ( 629 | 9CBB5C9C1CA3D9C30089E3EA /* Debug */, 630 | 9CBB5C9D1CA3D9C30089E3EA /* Release */, 631 | ); 632 | defaultConfigurationIsVisible = 0; 633 | defaultConfigurationName = Release; 634 | }; 635 | /* End XCConfigurationList section */ 636 | }; 637 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 638 | } 639 | -------------------------------------------------------------------------------- /Example/RxGesture.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /Example/RxGesture.xcodeproj/xcshareddata/xcschemes/RxGesture_iOS_Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 57 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 88 | 90 | 96 | 97 | 98 | 99 | 100 | 101 | 107 | 109 | 115 | 116 | 117 | 118 | 120 | 121 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /Example/RxGesture.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/RxGesture.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/RxGesture/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RxGesture 4 | // 5 | // Created by Marin Todorov on 03/22/2016. 6 | // Copyright (c) 2016 Marin Todorov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | } 15 | -------------------------------------------------------------------------------- /Example/RxGesture/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/RxGesture/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /Example/RxGesture/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/RxGesture/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example/RxGesture/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // RxGesture 4 | // 5 | // Created by Marin Todorov on 03/22/2016. 6 | // Copyright (c) 2016 Marin Todorov. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import RxSwift 12 | import RxGesture 13 | 14 | class Step { 15 | enum Action { case previous, next } 16 | typealias InitialState = (alpha: CGFloat, color: UIColor, transform: CGAffineTransform) 17 | let title: String 18 | let code: String 19 | let initialState: InitialState 20 | let install: (UIView, UILabel, @escaping () -> Void, DisposeBag) -> Void 21 | 22 | init(title: String, code: String, initialState: InitialState, install: @escaping (UIView, UILabel, @escaping () -> Void, DisposeBag) -> Void) { 23 | self.title = title 24 | self.code = code 25 | self.initialState = initialState 26 | self.install = install 27 | } 28 | } 29 | 30 | class ViewController: UIViewController { 31 | 32 | @IBOutlet private var myView: UIView! 33 | @IBOutlet private var myViewText: UILabel! 34 | @IBOutlet private var info: UILabel! 35 | @IBOutlet private var code: UITextView! 36 | 37 | private let nextStepObserver = PublishSubject() 38 | private let bag = DisposeBag() 39 | private var stepBag = DisposeBag() 40 | 41 | override func viewDidLoad() { 42 | super.viewDidLoad() 43 | 44 | navigationController?.navigationBar.shadowImage = UIImage() 45 | let steps: [Step] = [ 46 | tapStep, 47 | doubleTapStep, 48 | swipeDownStep, 49 | swipeHorizontallyStep, 50 | longPressStep, 51 | touchDownStep, 52 | forceTouchStep, 53 | panStep, 54 | pinchStep, 55 | rotateStep, 56 | transformStep 57 | ] 58 | 59 | func newIndex(for index: Int, action: Step.Action) -> Int { 60 | switch action { 61 | case .previous: 62 | return (steps.count + index - 1) % steps.count 63 | case .next: 64 | return (steps.count + index + 1) % steps.count 65 | } 66 | } 67 | 68 | nextStepObserver 69 | .scan(0, accumulator: newIndex) 70 | .startWith(0) 71 | .map { (steps[$0], $0) } 72 | .subscribe(onNext: { [unowned self] step, index in 73 | self.updateStep(step, at: index) 74 | }) 75 | .disposed(by: bag) 76 | } 77 | 78 | override func viewDidAppear(_ animated: Bool) { 79 | super.viewDidAppear(animated) 80 | guard let superview = myView.superview, let window = view.window else { 81 | return 82 | } 83 | window.addSubview(myView) 84 | myView.centerXAnchor.constraint(equalTo: superview.centerXAnchor).isActive = true 85 | myView.centerYAnchor.constraint(equalTo: superview.centerYAnchor).isActive = true 86 | } 87 | 88 | @IBAction func previousStep(_ sender: Any) { 89 | nextStepObserver.onNext(.previous) 90 | } 91 | 92 | @IBAction func nextStep(_ sender: Any) { 93 | nextStepObserver.onNext(.next) 94 | } 95 | 96 | func updateStep(_ step: Step, at index: Int) { 97 | stepBag = DisposeBag() 98 | 99 | info.text = "\(index + 1). " + step.title 100 | code.text = step.code 101 | 102 | myViewText.text = nil 103 | myViewText.numberOfLines = 1 104 | 105 | UIView.animate(withDuration: 0.5, delay: 0, options: .beginFromCurrentState) { 106 | self.myView.alpha = step.initialState.alpha 107 | self.myView.backgroundColor = step.initialState.color 108 | self.myView.transform = step.initialState.transform 109 | } 110 | 111 | step.install(myView, myViewText, { [nextStepObserver] in nextStepObserver.onNext(.next) }, stepBag) 112 | 113 | print("active gestures: \(myView.gestureRecognizers?.count ?? 0)") 114 | } 115 | 116 | lazy var tapStep: Step = Step( 117 | title: "Tap the red square", 118 | code: """ 119 | view.rx 120 | .tapGesture() 121 | .when(.recognized) 122 | .subscribe(onNext: { _ in 123 | // Do something 124 | }) 125 | .disposed(by: disposeBag) 126 | """, 127 | initialState: (1.0, .red, .identity), 128 | install: { view, _, nextStep, stepBag in 129 | view.rx 130 | .tapGesture() 131 | .when(.recognized) 132 | .subscribe(onNext: { _ in 133 | nextStep() 134 | }) 135 | .disposed(by: stepBag) 136 | }) 137 | 138 | lazy var doubleTapStep: Step = Step( 139 | title: "Double tap the green square", 140 | code: """ 141 | view.rx 142 | .tapGesture() { gesture, _ in 143 | gesture.numberOfTapsRequired = 2 144 | } 145 | .when(.recognized) 146 | .subscribe(onNext: { _ in 147 | // Do something 148 | }) 149 | .disposed(by: disposeBag) 150 | """, 151 | initialState: (1.0, .green, .identity), 152 | install: { view, _, nextStep, stepBag in 153 | view.rx 154 | .tapGesture { gesture, _ in 155 | gesture.numberOfTapsRequired = 2 156 | } 157 | .when(.recognized) 158 | .subscribe(onNext: { _ in 159 | nextStep() 160 | }) 161 | .disposed(by: stepBag) 162 | }) 163 | 164 | lazy var swipeDownStep: Step = Step( 165 | title: "Swipe the blue square down", 166 | code: """ 167 | view.rx 168 | .swipeGesture(.down) 169 | .when(.recognized) 170 | .subscribe(onNext: { _ in 171 | // Do something 172 | }) 173 | .disposed(by: disposeBag) 174 | """, 175 | initialState: (1.0, .blue, .identity), 176 | install: { view, _, nextStep, stepBag in 177 | view.rx 178 | .swipeGesture(.down) 179 | .when(.recognized) 180 | .subscribe(onNext: { _ in 181 | nextStep() 182 | }) 183 | .disposed(by: stepBag) 184 | }) 185 | 186 | lazy var swipeHorizontallyStep: Step = Step( 187 | title: "Swipe horizontally the blue square (e.g. left or right)", 188 | code: """ 189 | view.rx 190 | .swipeGesture(.left, .right) 191 | .when(.recognized) 192 | .subscribe(onNext: { _ in 193 | // Do something 194 | }) 195 | .disposed(by: disposeBag) 196 | """, 197 | initialState: (1.0, .blue, CGAffineTransform(scaleX: 1.0, y: 2.0)), 198 | install: { view, _, nextStep, stepBag in 199 | view.rx 200 | .swipeGesture(.left, .right) 201 | .when(.recognized) 202 | .subscribe(onNext: { _ in 203 | nextStep() 204 | }) 205 | .disposed(by: stepBag) 206 | }) 207 | 208 | lazy var longPressStep: Step = Step( 209 | title: "Do a long press", 210 | code: """ 211 | view.rx 212 | .longPressGesture() 213 | .when(.began) 214 | .subscribe(onNext: { _ in 215 | // Do something 216 | }) 217 | .disposed(by: disposeBag) 218 | """, 219 | initialState: (1.0, .blue, CGAffineTransform(scaleX: 2.0, y: 2.0)), 220 | install: { view, _, nextStep, stepBag in 221 | view.rx 222 | .longPressGesture() 223 | .when(.began) 224 | .subscribe(onNext: { _ in 225 | nextStep() 226 | }) 227 | .disposed(by: stepBag) 228 | }) 229 | 230 | lazy var touchDownStep: Step = Step( 231 | title: "Touch down the view", 232 | code: """ 233 | view.rx 234 | .touchDownGesture() 235 | .when(.began) 236 | .subscribe(onNext: { _ in 237 | // Do something 238 | }) 239 | .disposed(by: disposeBag) 240 | """, 241 | initialState: (1.0, .green, .identity), 242 | install: { view, _, nextStep, stepBag in 243 | view.rx 244 | .touchDownGesture() 245 | .when(.began) 246 | .subscribe(onNext: { _ in 247 | nextStep() 248 | }) 249 | .disposed(by: stepBag) 250 | }) 251 | 252 | @available(iOS 9.0, *) 253 | lazy var forceTouchStep: Step = Step( 254 | title: "Force Touch the view", 255 | code: """ 256 | let forceTouch = view.rx 257 | .forceTouchGesture() 258 | .share(replay: 1) 259 | 260 | forceTouch 261 | .asForce() 262 | .subscribe(onNext: { force in 263 | // Do something 264 | }) 265 | .disposed(by: stepBag) 266 | 267 | forceTouch 268 | .when(.ended) 269 | .subscribe(onNext: { _ in 270 | // Do something 271 | }) 272 | .disposed(by: stepBag) 273 | """, 274 | initialState: (0.25, .red, .identity), 275 | install: { view, label, nextStep, stepBag in 276 | let forceTouch = view.rx 277 | .forceTouchGesture() 278 | .share(replay: 1) 279 | 280 | forceTouch 281 | .when(.possible, .began, .changed) 282 | .subscribe(onNext: { [unowned view] touch in 283 | let max = touch.maximumPossibleForce 284 | let percent = max > 0 ? touch.force / max : 0 285 | view.alpha = percent > 0.75 ? 1.0 : 0.25 + (0.5 * percent) 286 | label.text = String(format: "%.0f%%", percent * 100) 287 | }) 288 | .disposed(by: stepBag) 289 | 290 | forceTouch 291 | .when(.ended) 292 | .subscribe(onNext: { _ in 293 | nextStep() 294 | }) 295 | .disposed(by: stepBag) 296 | 297 | self.makeImpact(on: forceTouch, stepBag: stepBag) 298 | }) 299 | 300 | lazy var panStep: Step = Step( 301 | title: "Drag the square to a different location", 302 | code: """ 303 | let panGesture = view.rx 304 | .panGesture() 305 | .share(replay: 1) 306 | 307 | panGesture 308 | .when(.changed) 309 | .asTranslation() 310 | .subscribe(onNext: { _ in 311 | // Do something 312 | }) 313 | .disposed(by: disposeBag) 314 | 315 | panGesture 316 | .when(.ended) 317 | .subscribe(onNext: { _ in 318 | // Do something 319 | }) 320 | .disposed(by: disposeBag) 321 | """, 322 | initialState: (1.0, .blue, .identity), 323 | install: { view, label, nextStep, stepBag in 324 | let panGesture = view.rx 325 | .panGesture() 326 | .share(replay: 1) 327 | 328 | panGesture 329 | .when(.possible, .began, .changed) 330 | .asTranslation() 331 | .subscribe(onNext: { translation, _ in 332 | label.text = String(format: "(%.2f, %.2f)", translation.x, translation.y) 333 | view.transform = CGAffineTransform(translationX: translation.x, y: translation.y) 334 | }) 335 | .disposed(by: stepBag) 336 | 337 | panGesture 338 | .when(.ended) 339 | .subscribe(onNext: { _ in 340 | nextStep() 341 | }) 342 | .disposed(by: stepBag) 343 | }) 344 | 345 | lazy var rotateStep: Step = Step( 346 | title: "Rotate the square", 347 | code: """ 348 | let rotationGesture = view.rx 349 | .rotationGesture() 350 | .share(replay: 1) 351 | 352 | rotationGesture 353 | .when(.changed) 354 | .asRotation() 355 | .subscribe(onNext: { _ in 356 | // Do something 357 | }) 358 | .disposed(by: disposeBag) 359 | 360 | rotationGesture 361 | .when(.ended) 362 | .subscribe(onNext: { _ in 363 | // Do something 364 | }) 365 | .disposed(by: disposeBag) 366 | """, 367 | initialState: (1.0, .blue, .identity), 368 | install: { view, label, nextStep, stepBag in 369 | let rotationGesture = view.rx 370 | .rotationGesture() 371 | .share(replay: 1) 372 | 373 | rotationGesture 374 | .when(.possible, .began, .changed) 375 | .asRotation() 376 | .subscribe(onNext: { rotation, _ in 377 | label.text = String(format: "%.fº", rotation * 180 / .pi) 378 | view.transform = CGAffineTransform(rotationAngle: rotation) 379 | }) 380 | .disposed(by: stepBag) 381 | 382 | rotationGesture 383 | .when(.ended) 384 | .subscribe(onNext: { _ in 385 | nextStep() 386 | }) 387 | .disposed(by: stepBag) 388 | }) 389 | 390 | lazy var pinchStep: Step = Step( 391 | title: "Pinch the square", 392 | code: """ 393 | let pinchGesture = view.rx.pinchGesture().share(replay: 1) 394 | 395 | pinchGesture 396 | .when(.changed) 397 | .asScale() 398 | .subscribe(onNext: { _ in 399 | // Do something 400 | }) 401 | .disposed(by: disposeBag) 402 | 403 | pinchGesture 404 | .when(.ended) 405 | .subscribe(onNext: { _ in 406 | // Do something 407 | }) 408 | .disposed(by: disposeBag) 409 | """, 410 | initialState: (1.0, .blue, .identity), 411 | install: { view, label, nextStep, stepBag in 412 | let pinchGesture = view.rx 413 | .pinchGesture() 414 | .share(replay: 1) 415 | 416 | pinchGesture 417 | .when(.possible, .began, .changed) 418 | .asScale() 419 | .subscribe(onNext: { scale, _ in 420 | label.text = String(format: "x%.2f", scale) 421 | view.transform = CGAffineTransform(scaleX: scale, y: scale) 422 | }) 423 | .disposed(by: stepBag) 424 | 425 | pinchGesture 426 | .when(.ended) 427 | .subscribe(onNext: { _ in 428 | nextStep() 429 | }) 430 | .disposed(by: stepBag) 431 | }) 432 | 433 | lazy var transformStep: Step = Step( 434 | title: "Transform the square", 435 | code: """ 436 | let transformGestures = view.rx.transformGestures().share(replay: 1) 437 | 438 | transformGestures 439 | .when(.changed) 440 | .asTransform() 441 | .subscribe(onNext: { _ in 442 | // Do something 443 | }) 444 | .disposed(by: disposeBag) 445 | 446 | transformGestures 447 | .when(.ended) 448 | .subscribe(onNext: { _ in 449 | // Do something 450 | }) 451 | .disposed(by: disposeBag) 452 | """, 453 | initialState: (1.0, .blue, .identity), 454 | install: { view, label, nextStep, stepBag in 455 | let transformGestures = view.rx 456 | .transformGestures() 457 | .share(replay: 1) 458 | 459 | transformGestures 460 | .when(.possible, .began, .changed) 461 | .asTransform() 462 | .subscribe(onNext: { transform, _ in 463 | label.numberOfLines = 3 464 | label.text = String(format: "[%.2f, %.2f,\n%.2f, %.2f,\n%.2f, %.2f]", transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty) 465 | view.transform = transform 466 | }) 467 | .disposed(by: stepBag) 468 | 469 | transformGestures 470 | .when(.ended) 471 | .subscribe(onNext: { _ in 472 | nextStep() 473 | }) 474 | .disposed(by: stepBag) 475 | }) 476 | 477 | private func makeImpact(on forceTouch: Observable, stepBag: DisposeBag) { 478 | // It looks like #available(iOS 10.0, *) is ignored in the lazy var declaration ¯\_(ツ)_/¯ 479 | 480 | guard #available(iOS 10.0, *) else { return } 481 | forceTouch 482 | .map { ($0.force / $0.maximumPossibleForce) > 0.75 ? UIImpactFeedbackGenerator.FeedbackStyle.medium : .light } 483 | .distinctUntilChanged() 484 | .skip(1) 485 | .subscribe(onNext: { style in 486 | UIImpactFeedbackGenerator(style: style).impactOccurred() 487 | }) 488 | .disposed(by: stepBag) 489 | } 490 | } 491 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'cocoapods', '~> 1.10' -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.3) 5 | activesupport (5.2.4.4) 6 | concurrent-ruby (~> 1.0, >= 1.0.2) 7 | i18n (>= 0.7, < 2) 8 | minitest (~> 5.1) 9 | tzinfo (~> 1.1) 10 | addressable (2.8.0) 11 | public_suffix (>= 2.0.2, < 5.0) 12 | algoliasearch (1.27.5) 13 | httpclient (~> 2.8, >= 2.8.3) 14 | json (>= 1.5.1) 15 | atomos (0.1.3) 16 | claide (1.0.3) 17 | cocoapods (1.10.0) 18 | addressable (~> 2.6) 19 | claide (>= 1.0.2, < 2.0) 20 | cocoapods-core (= 1.10.0) 21 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 22 | cocoapods-downloader (>= 1.4.0, < 2.0) 23 | cocoapods-plugins (>= 1.0.0, < 2.0) 24 | cocoapods-search (>= 1.0.0, < 2.0) 25 | cocoapods-trunk (>= 1.4.0, < 2.0) 26 | cocoapods-try (>= 1.1.0, < 2.0) 27 | colored2 (~> 3.1) 28 | escape (~> 0.0.4) 29 | fourflusher (>= 2.3.0, < 3.0) 30 | gh_inspector (~> 1.0) 31 | molinillo (~> 0.6.6) 32 | nap (~> 1.0) 33 | ruby-macho (~> 1.4) 34 | xcodeproj (>= 1.19.0, < 2.0) 35 | cocoapods-core (1.10.0) 36 | activesupport (> 5.0, < 6) 37 | addressable (~> 2.6) 38 | algoliasearch (~> 1.0) 39 | concurrent-ruby (~> 1.1) 40 | fuzzy_match (~> 2.0.4) 41 | nap (~> 1.0) 42 | netrc (~> 0.11) 43 | public_suffix 44 | typhoeus (~> 1.0) 45 | cocoapods-deintegrate (1.0.4) 46 | cocoapods-downloader (1.6.3) 47 | cocoapods-plugins (1.0.0) 48 | nap 49 | cocoapods-search (1.0.0) 50 | cocoapods-trunk (1.5.0) 51 | nap (>= 0.8, < 2.0) 52 | netrc (~> 0.11) 53 | cocoapods-try (1.2.0) 54 | colored2 (3.1.2) 55 | concurrent-ruby (1.1.7) 56 | escape (0.0.4) 57 | ethon (0.12.0) 58 | ffi (>= 1.3.0) 59 | ffi (1.14.2) 60 | fourflusher (2.3.1) 61 | fuzzy_match (2.0.4) 62 | gh_inspector (1.1.3) 63 | httpclient (2.8.3) 64 | i18n (1.8.6) 65 | concurrent-ruby (~> 1.0) 66 | json (2.5.1) 67 | minitest (5.14.2) 68 | molinillo (0.6.6) 69 | nanaimo (0.3.0) 70 | nap (1.1.0) 71 | netrc (0.11.0) 72 | public_suffix (4.0.6) 73 | ruby-macho (1.4.0) 74 | thread_safe (0.3.6) 75 | typhoeus (1.4.0) 76 | ethon (>= 0.9.0) 77 | tzinfo (1.2.9) 78 | thread_safe (~> 0.1) 79 | xcodeproj (1.19.0) 80 | CFPropertyList (>= 2.3.3, < 4.0) 81 | atomos (~> 0.1.3) 82 | claide (>= 1.0.2, < 2.0) 83 | colored2 (~> 3.1) 84 | nanaimo (~> 0.3.0) 85 | 86 | PLATFORMS 87 | ruby 88 | 89 | DEPENDENCIES 90 | cocoapods (~> 1.10) 91 | 92 | BUNDLED WITH 93 | 2.2.4 94 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) RxSwiftCommunity 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "RxGesture", 6 | products: [ 7 | .library(name: "RxGesture", targets: ["RxGesture"]) 8 | ], 9 | dependencies: [ 10 | .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.0.0")), 11 | ], 12 | targets: [ 13 | .target( 14 | name: "RxGesture", 15 | dependencies: ["RxSwift", "RxCocoa"], 16 | path: "Pod", 17 | exclude: ["Classes/OSX"] 18 | ) 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /Pod/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxGesture/e9f94dfd39efe5b2914ac9463d4193b9e340779c/Pod/Assets/.gitkeep -------------------------------------------------------------------------------- /Pod/Assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxGesture/e9f94dfd39efe5b2914ac9463d4193b9e340779c/Pod/Assets/demo.gif -------------------------------------------------------------------------------- /Pod/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxGesture/e9f94dfd39efe5b2914ac9463d4193b9e340779c/Pod/Classes/.gitkeep -------------------------------------------------------------------------------- /Pod/Classes/GenericRxGestureRecognizerDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #if os(iOS) 22 | import UIKit 23 | #elseif os(OSX) 24 | import AppKit 25 | #endif 26 | import RxSwift 27 | import RxCocoa 28 | 29 | public struct GestureRecognizerDelegatePolicy { 30 | public typealias PolicyBody = (PolicyInput) -> Bool 31 | 32 | private let policy: PolicyBody 33 | 34 | private init(policy: @escaping PolicyBody) { 35 | self.policy = policy 36 | } 37 | 38 | public static func custom(_ policy: @escaping PolicyBody) 39 | -> GestureRecognizerDelegatePolicy { 40 | .init(policy: policy) 41 | } 42 | 43 | public static var always: GestureRecognizerDelegatePolicy { 44 | .init { _ in true } 45 | } 46 | 47 | public static var never: GestureRecognizerDelegatePolicy { 48 | .init { _ in false } 49 | } 50 | 51 | public func isPolicyPassing(with args: PolicyInput) -> Bool { 52 | policy(args) 53 | } 54 | } 55 | 56 | public func || (lhs: GestureRecognizerDelegatePolicy, rhs: GestureRecognizerDelegatePolicy) -> GestureRecognizerDelegatePolicy { 57 | .custom { input in 58 | lhs.isPolicyPassing(with: input) || rhs.isPolicyPassing(with: input) 59 | } 60 | } 61 | 62 | public func && (lhs: GestureRecognizerDelegatePolicy, rhs: GestureRecognizerDelegatePolicy) -> GestureRecognizerDelegatePolicy { 63 | .custom { input in 64 | lhs.isPolicyPassing(with: input) && rhs.isPolicyPassing(with: input) 65 | } 66 | } 67 | 68 | public final class GenericRxGestureRecognizerDelegate: NSObject, RxGestureRecognizerDelegate { 69 | 70 | /// Corresponding delegate method: gestureRecognizerShouldBegin(:_) 71 | public var beginPolicy: GestureRecognizerDelegatePolicy = .always 72 | 73 | /// Corresponding delegate method: gestureRecognizer(_:shouldReceive:) 74 | public var touchReceptionPolicy: GestureRecognizerDelegatePolicy<(Gesture, RxGestureTouch)> = .always 75 | 76 | /// Corresponding delegate method: gestureRecognizer(_:shouldBeRequiredToFailBy:) 77 | public var selfFailureRequirementPolicy: GestureRecognizerDelegatePolicy<(Gesture, RxGestureRecognizer)> = .never 78 | 79 | /// Corresponding delegate method: gestureRecognizer(_:shouldRequireFailureOf:) 80 | public var otherFailureRequirementPolicy: GestureRecognizerDelegatePolicy<(Gesture, RxGestureRecognizer)> = .never 81 | 82 | /// Corresponding delegate method: gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) 83 | public var simultaneousRecognitionPolicy: GestureRecognizerDelegatePolicy<(Gesture, RxGestureRecognizer)> = .always 84 | 85 | #if os(iOS) 86 | // Workaround because we can't have stored properties with @available annotation 87 | private var _pressReceptionPolicy: Any? 88 | 89 | /// Corresponding delegate method: gestureRecognizer(_:shouldReceive:) 90 | public var pressReceptionPolicy: GestureRecognizerDelegatePolicy<(Gesture, UIPress)> { 91 | get { 92 | _pressReceptionPolicy as? GestureRecognizerDelegatePolicy<(Gesture, UIPress)> ?? .always 93 | } 94 | set { 95 | _pressReceptionPolicy = newValue 96 | } 97 | } 98 | #endif 99 | 100 | #if os(OSX) 101 | /// Corresponding delegate method: gestureRecognizer(_:shouldAttemptToRecognizeWith:) 102 | public var eventRecognitionAttemptPolicy: GestureRecognizerDelegatePolicy<(Gesture, NSEvent)> = .always 103 | #endif 104 | 105 | public func gestureRecognizerShouldBegin( 106 | _ gestureRecognizer: RxGestureRecognizer 107 | ) -> Bool { 108 | beginPolicy.isPolicyPassing(with: gestureRecognizer as! Gesture) 109 | } 110 | 111 | public func gestureRecognizer( 112 | _ gestureRecognizer: RxGestureRecognizer, 113 | shouldReceive touch: RxGestureTouch 114 | ) -> Bool { 115 | touchReceptionPolicy.isPolicyPassing( 116 | with: (gestureRecognizer as! Gesture, touch) 117 | ) 118 | } 119 | 120 | public func gestureRecognizer( 121 | _ gestureRecognizer: RxGestureRecognizer, 122 | shouldRequireFailureOf otherGestureRecognizer: RxGestureRecognizer 123 | ) -> Bool { 124 | otherFailureRequirementPolicy.isPolicyPassing( 125 | with: (gestureRecognizer as! Gesture, otherGestureRecognizer) 126 | ) 127 | } 128 | 129 | public func gestureRecognizer( 130 | _ gestureRecognizer: RxGestureRecognizer, 131 | shouldBeRequiredToFailBy otherGestureRecognizer: RxGestureRecognizer 132 | ) -> Bool { 133 | selfFailureRequirementPolicy.isPolicyPassing( 134 | with: (gestureRecognizer as! Gesture, otherGestureRecognizer) 135 | ) 136 | } 137 | 138 | public func gestureRecognizer( 139 | _ gestureRecognizer: RxGestureRecognizer, 140 | shouldRecognizeSimultaneouslyWith otherGestureRecognizer: RxGestureRecognizer 141 | ) -> Bool { 142 | simultaneousRecognitionPolicy.isPolicyPassing( 143 | with: (gestureRecognizer as! Gesture, otherGestureRecognizer) 144 | ) 145 | } 146 | 147 | #if os(iOS) 148 | 149 | public func gestureRecognizer( 150 | _ gestureRecognizer: RxGestureRecognizer, 151 | shouldReceive press: UIPress 152 | ) -> Bool { 153 | pressReceptionPolicy.isPolicyPassing( 154 | with: (gestureRecognizer as! Gesture, press) 155 | ) 156 | } 157 | 158 | #endif 159 | 160 | #if os(OSX) 161 | 162 | public func gestureRecognizer( 163 | _ gestureRecognizer: RxGestureRecognizer, 164 | shouldAttemptToRecognizeWith event: NSEvent 165 | ) -> Bool { 166 | eventRecognitionAttemptPolicy.isPolicyPassing( 167 | with: (gestureRecognizer as! Gesture, event) 168 | ) 169 | } 170 | 171 | #endif 172 | 173 | } 174 | -------------------------------------------------------------------------------- /Pod/Classes/GestureFactory.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | #if canImport(ObjectiveC) 21 | import RxSwift 22 | import RxCocoa 23 | import ObjectiveC 24 | 25 | public typealias Configuration = (Gesture, GenericRxGestureRecognizerDelegate) -> Void 26 | 27 | public struct Factory { 28 | public let gesture: Gesture 29 | public init(_ configuration: Configuration?) { 30 | let gesture = Gesture() 31 | let delegate = GenericRxGestureRecognizerDelegate() 32 | objc_setAssociatedObject( 33 | gesture, 34 | &gestureRecognizerStrongDelegateKey, 35 | delegate, 36 | .OBJC_ASSOCIATION_RETAIN_NONATOMIC 37 | ) 38 | gesture.delegate = delegate 39 | configuration?(gesture, delegate) 40 | self.gesture = gesture 41 | } 42 | 43 | internal func abstracted() -> AnyFactory { 44 | AnyFactory(self.gesture) 45 | } 46 | } 47 | 48 | internal func make(configuration: Configuration? = nil) -> Factory { 49 | Factory(configuration) 50 | } 51 | 52 | public typealias AnyFactory = Factory 53 | extension Factory where Gesture == RxGestureRecognizer { 54 | private init(_ gesture: G) { 55 | self.gesture = gesture 56 | } 57 | } 58 | 59 | private var gestureRecognizerStrongDelegateKey: UInt8 = 0 60 | #endif 61 | -------------------------------------------------------------------------------- /Pod/Classes/GestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | import RxSwift 22 | import RxCocoa 23 | import struct CoreGraphics.CGPoint 24 | 25 | public typealias LocationInView = (RxGestureView) -> CGPoint 26 | 27 | extension ObservableType where Element: RxGestureRecognizer { 28 | 29 | /** 30 | Filters the observable `GestureRecognizer` events sequence based on the `GestureRecognizer` state. 31 | 32 | - parameter state: An `GestureRecognizerState` that is used to filter the `GestureRecognizer` events sequence. 33 | - returns: An observable `GestureRecognizer` events sequence that only contains events emitted while the `GestureRecognizer`'s state match the given `state`. 34 | */ 35 | public func when(_ states: RxGestureRecognizerState...) -> Observable { 36 | filter { gesture in 37 | states.contains(gesture.state) 38 | } 39 | } 40 | 41 | /** 42 | Filters the observable `GestureRecognizer` events sequence based on the `GestureRecognizer` state. 43 | 44 | - parameter state: An `GestureRecognizerState` that is used to filter the `GestureRecognizer` events sequence. 45 | - returns: An observable `GestureRecognizer` events sequence that only contains events emitted while the `GestureRecognizer`'s state match the given `state`. 46 | */ 47 | internal func when(_ states: [RxGestureRecognizerState]) -> Observable { 48 | filter { gesture in 49 | states.contains(gesture.state) 50 | } 51 | } 52 | 53 | /** 54 | Maps the observable `GestureRecognizer` events sequence to an observable sequence of points computed as the location in the given `view` of the gesture. 55 | 56 | - parameter view: A `TargetView` value on which the gesture took place. 57 | */ 58 | public func asLocation(in view: TargetView = .view) -> Observable { 59 | map { gesture in 60 | gesture.location(in: view.targetView(for: gesture)) 61 | } 62 | } 63 | 64 | public func asLocationInView() -> Observable { 65 | map { gesture in 66 | let targetView = gesture.view! 67 | let location = gesture.location(in: targetView) 68 | return { view in 69 | targetView.convert(location, to: view) 70 | } 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Pod/Classes/OSX/NSClickGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 21 | import AppKit 22 | import RxSwift 23 | import RxCocoa 24 | 25 | private func make(mask: Int, configuration: Configuration?) -> Factory { 26 | make { 27 | $0.buttonMask = mask 28 | configuration?($0, $1) 29 | } 30 | } 31 | 32 | public typealias ClickConfiguration = Configuration 33 | public typealias ClickControlEvent = ControlEvent 34 | public typealias ClickObservable = Observable 35 | 36 | extension Factory where Gesture == RxGestureRecognizer { 37 | 38 | /** 39 | Returns an `AnyFactory` for `NSClickGestureRecognizer` 40 | - parameter buttonMask: bitfield of the button(s) required to recognize this click where bit 0 is the primary button, 1 is the secondary button, etc... 41 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 42 | */ 43 | public static func click(buttonMask: Int, configuration: ClickConfiguration? = nil) -> AnyFactory { 44 | make(mask: buttonMask, configuration: configuration).abstracted() 45 | } 46 | 47 | /** 48 | Returns an `AnyFactory` for `NSClickGestureRecognizer` 49 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 50 | */ 51 | public static func leftClick(configuration: ClickConfiguration? = nil) -> AnyFactory { 52 | click(buttonMask: 0x1, configuration: configuration) 53 | } 54 | 55 | /** 56 | Returns an `AnyFactory` for `NSClickGestureRecognizer` 57 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 58 | */ 59 | public static func rightClick(configuration: ClickConfiguration? = nil) -> AnyFactory { 60 | click(buttonMask: 0x2, configuration: configuration) 61 | } 62 | } 63 | 64 | extension Reactive where Base: RxGestureView { 65 | 66 | /** 67 | Returns an observable `NSClickGestureRecognizer` events sequence 68 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 69 | */ 70 | public func clickGesture(buttonMask: Int, configuration: ClickConfiguration? = nil) -> ClickControlEvent { 71 | gesture(make(mask: buttonMask, configuration: configuration)) 72 | } 73 | 74 | /** 75 | Returns an observable `NSClickGestureRecognizer` events sequence 76 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 77 | */ 78 | public func leftClickGesture(configuration: ClickConfiguration? = nil) -> ClickControlEvent { 79 | gesture(make(mask: 0x1, configuration: configuration)) 80 | } 81 | 82 | /** 83 | Returns an observable `NSClickGestureRecognizer` events sequence 84 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 85 | */ 86 | public func rightClickGesture(configuration: ClickConfiguration? = nil) -> ClickControlEvent { 87 | gesture(make(mask: 0x2, configuration: configuration)) 88 | } 89 | 90 | } 91 | #endif 92 | -------------------------------------------------------------------------------- /Pod/Classes/OSX/NSGestureRecognizer+Rx.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 21 | import AppKit 22 | import RxSwift 23 | import RxCocoa 24 | 25 | private class GestureTarget { 26 | 27 | private var retainedSelf: GestureTarget? 28 | 29 | var handler: (() -> Void)? 30 | 31 | init() { 32 | retainedSelf = self 33 | } 34 | 35 | func dispose() { 36 | retainedSelf = nil 37 | } 38 | 39 | @objc func controlEvent() { 40 | handler?() 41 | } 42 | } 43 | 44 | extension Reactive where Base: NSGestureRecognizer { 45 | 46 | /** 47 | Reactive wrapper for gesture recognizer events. 48 | */ 49 | public var event: ControlEvent { 50 | let source: Observable = Observable.create {observer in 51 | MainScheduler.ensureExecutingOnScheduler() 52 | 53 | let control = self.base 54 | control.isEnabled = true 55 | 56 | let gestureTarget = GestureTarget() 57 | gestureTarget.handler = { 58 | observer.on(.next(control)) 59 | } 60 | 61 | control.target = gestureTarget 62 | control.action = #selector(GestureTarget.controlEvent) 63 | 64 | return Disposables.create { 65 | if let view = control.view { 66 | view.removeGestureRecognizer(control) 67 | } 68 | gestureTarget.dispose() 69 | } 70 | }.take(until: self.deallocated) 71 | 72 | return ControlEvent(events: source) 73 | } 74 | 75 | } 76 | #endif 77 | -------------------------------------------------------------------------------- /Pod/Classes/OSX/NSMagnificationGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 21 | import AppKit 22 | import RxSwift 23 | import RxCocoa 24 | 25 | public typealias MagnificationConfiguration = Configuration 26 | public typealias MagnificationControlEvent = ControlEvent 27 | public typealias MagnificationObservable = Observable 28 | 29 | extension Factory where Gesture == RxGestureRecognizer { 30 | 31 | /** 32 | Returns an `AnyFactory` for `NSMagnificationGestureRecognizer` 33 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 34 | */ 35 | public static func magnification(configuration: MagnificationConfiguration? = nil) -> AnyFactory { 36 | make(configuration: configuration).abstracted() 37 | } 38 | } 39 | 40 | extension Reactive where Base: RxGestureView { 41 | 42 | /** 43 | Returns an observable `NSMagnificationGestureRecognizer` events sequence 44 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 45 | */ 46 | public func magnificationGesture(configuration: MagnificationConfiguration? = nil) -> MagnificationControlEvent { 47 | gesture(make(configuration: configuration)) 48 | } 49 | } 50 | 51 | extension ObservableType where Element: NSMagnificationGestureRecognizer { 52 | 53 | /** 54 | Maps the observable `GestureRecognizer` events sequence to an observable sequence of magnification amounts alongside the gesture velocity. 55 | */ 56 | public func asMagnification() -> Observable { 57 | self.map { gesture in 58 | gesture.magnification 59 | } 60 | } 61 | 62 | /** 63 | Maps the observable `GestureRecognizer` events sequence to an observable sequence of scale factors relative to the points of the two touches in screen coordinates alongside the gesture velocity. 64 | */ 65 | public func asScale() -> Observable { 66 | self.map { gesture in 67 | 1.0 + gesture.magnification 68 | } 69 | } 70 | } 71 | #endif 72 | -------------------------------------------------------------------------------- /Pod/Classes/OSX/NSPanGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 21 | import AppKit 22 | import RxSwift 23 | import RxCocoa 24 | 25 | public typealias PanConfiguration = Configuration 26 | public typealias PanControlEvent = ControlEvent 27 | public typealias PanObservable = Observable 28 | 29 | extension Factory where Gesture == RxGestureRecognizer { 30 | 31 | /** 32 | Returns an `AnyFactory` for `NSPanGestureRecognizer` 33 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 34 | */ 35 | public static func pan(configuration: PanConfiguration? = nil) -> AnyFactory { 36 | make(configuration: configuration).abstracted() 37 | } 38 | } 39 | 40 | extension Reactive where Base: RxGestureView { 41 | 42 | /** 43 | Returns an observable `NSPanGestureRecognizer` events sequence 44 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 45 | */ 46 | public func panGesture(configuration: PanConfiguration? = nil) -> PanControlEvent { 47 | gesture(make(configuration: configuration)) 48 | } 49 | } 50 | 51 | extension ObservableType where Element: NSPanGestureRecognizer { 52 | 53 | /** 54 | Maps the observable `GestureRecognizer` events sequence to an observable sequence of translation values of the pan gesture in the coordinate system of the specified `view` alongside the gesture velocity. 55 | 56 | - parameter view: A `TargetView` value on which the gesture took place. 57 | */ 58 | public func asTranslation(in view: TargetView = .view) -> Observable<(translation: NSPoint, velocity: NSPoint)> { 59 | self.map { gesture in 60 | let view = view.targetView(for: gesture) 61 | return ( 62 | gesture.translation(in: view), 63 | gesture.velocity(in: view) 64 | ) 65 | } 66 | } 67 | } 68 | #endif 69 | -------------------------------------------------------------------------------- /Pod/Classes/OSX/NSPressGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 21 | import AppKit 22 | import RxSwift 23 | import RxCocoa 24 | 25 | public typealias PressConfiguration = Configuration 26 | public typealias PressControlEvent = ControlEvent 27 | public typealias PressObservable = Observable 28 | 29 | extension Factory where Gesture == RxGestureRecognizer { 30 | 31 | /** 32 | Returns an `AnyFactory` for `NSPressGestureRecognizer` 33 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 34 | */ 35 | public static func press(configuration: PressConfiguration? = nil) -> AnyFactory { 36 | make(configuration: configuration).abstracted() 37 | } 38 | } 39 | 40 | extension Reactive where Base: RxGestureView { 41 | 42 | /** 43 | Returns an observable `NSPressGestureRecognizer` events sequence 44 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 45 | */ 46 | public func pressGesture(configuration: PressConfiguration? = nil) -> PressControlEvent { 47 | gesture(make(configuration: configuration)) 48 | } 49 | } 50 | #endif 51 | -------------------------------------------------------------------------------- /Pod/Classes/OSX/NSRotationGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 21 | import AppKit 22 | import RxSwift 23 | import RxCocoa 24 | 25 | public typealias RotationConfiguration = Configuration 26 | public typealias RotationControlEvent = ControlEvent 27 | public typealias RotationObservable = Observable 28 | 29 | extension Factory where Gesture == RxGestureRecognizer { 30 | 31 | /** 32 | Returns an `AnyFactory` for `NSRotationGestureRecognizer` 33 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 34 | */ 35 | public static func rotation(configuration: RotationConfiguration? = nil) -> AnyFactory { 36 | make(configuration: configuration).abstracted() 37 | } 38 | } 39 | 40 | extension Reactive where Base: RxGestureView { 41 | 42 | /** 43 | Returns an observable `NSRotationGestureRecognizer` events sequence 44 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 45 | */ 46 | public func rotationGesture(configuration: RotationConfiguration? = nil) -> RotationControlEvent { 47 | gesture(make(configuration: configuration)) 48 | } 49 | } 50 | 51 | extension ObservableType where Element: NSRotationGestureRecognizer { 52 | 53 | /** 54 | Maps the observable `GestureRecognizer` events sequence to an observable sequence of rotation values of the gesture in radians. 55 | */ 56 | public func asRotation() -> Observable { 57 | self.map { gesture in 58 | gesture.rotation 59 | } 60 | } 61 | } 62 | #endif 63 | -------------------------------------------------------------------------------- /Pod/Classes/SharedTypes.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | import Foundation 22 | 23 | #if os(iOS) 24 | import UIKit 25 | public typealias RxGestureTouch = UITouch 26 | public typealias RxGestureRecognizer = UIGestureRecognizer 27 | public typealias RxGestureRecognizerState = UIGestureRecognizer.State 28 | public typealias RxGestureRecognizerDelegate = UIGestureRecognizerDelegate 29 | public typealias RxGestureView = UIView 30 | public typealias RxGesturePoint = CGPoint 31 | #elseif os(OSX) 32 | import AppKit 33 | public typealias RxGestureTouch = NSTouch 34 | public typealias RxGestureRecognizer = NSGestureRecognizer 35 | public typealias RxGestureRecognizerState = NSGestureRecognizer.State 36 | public typealias RxGestureRecognizerDelegate = NSGestureRecognizerDelegate 37 | public typealias RxGestureView = NSView 38 | public typealias RxGesturePoint = NSPoint 39 | #endif 40 | 41 | public enum TargetView { 42 | /// The target view will be the gestureRecognizer's view 43 | case view 44 | 45 | /// The target view will be the gestureRecognizer's view's superview 46 | case superview 47 | 48 | /// The target view will be the gestureRecognizer's view's window 49 | case window 50 | 51 | /// The target view will be the given view 52 | case this(RxGestureView) 53 | 54 | public func targetView(for gestureRecognizer: RxGestureRecognizer) -> RxGestureView? { 55 | switch self { 56 | case .view: 57 | return gestureRecognizer.view 58 | case .superview: 59 | return gestureRecognizer.view?.superview 60 | case .window: 61 | #if os(iOS) 62 | return gestureRecognizer.view?.window 63 | #elseif os(OSX) 64 | return gestureRecognizer.view?.window?.contentView 65 | #endif 66 | case let .this(view): 67 | return view 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Pod/Classes/View+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | import RxSwift 22 | import RxCocoa 23 | import Dispatch 24 | 25 | extension Reactive where Base: RxGestureView { 26 | 27 | /** 28 | Reactive wrapper for multiple view gesture recognizers. 29 | It automatically attaches the gesture recognizers to the receiver view. 30 | The value the `Observable` emits is the gesture recognizer itself. 31 | 32 | rx.anyGesture can't error and is subscribed/observed on main scheduler. 33 | - parameter factories: a `(Factory + state)` collection you want to use to create the `GestureRecognizers` to add and observe 34 | - returns: a `ControlEvent` that re-emit the gesture recognizer itself 35 | */ 36 | public func anyGesture(_ factories: (AnyFactory, when: RxGestureRecognizerState)...) -> ControlEvent { 37 | let observables = factories.map { gesture, state in 38 | self.gesture(gesture).when(state).asObservable() as Observable 39 | } 40 | let source = Observable.from(observables).merge() 41 | return ControlEvent(events: source) 42 | } 43 | 44 | /** 45 | Reactive wrapper for multiple view gesture recognizers. 46 | It automatically attaches the gesture recognizers to the receiver view. 47 | The value the `Observable` emits is the gesture recognizer itself. 48 | 49 | rx.anyGesture can't error and is subscribed/observed on main scheduler. 50 | - parameter factories: a `Factory` collection you want to use to create the `GestureRecognizers` to add and observe 51 | - returns: a `ControlEvent` that re-emit the gesture recognizer itself 52 | */ 53 | public func anyGesture(_ factories: AnyFactory...) -> ControlEvent { 54 | let observables = factories.map { factory in 55 | self.gesture(factory).asObservable() as Observable 56 | } 57 | let source = Observable.from(observables).merge() 58 | return ControlEvent(events: source) 59 | } 60 | 61 | /** 62 | Reactive wrapper for a single view gesture recognizer. 63 | It automatically attaches the gesture recognizer to the receiver view. 64 | The value the `Observable` emits is the gesture recognizer itself. 65 | 66 | rx.gesture can't error and is subscribed/observed on main scheduler. 67 | - parameter factory: a `Factory` you want to use to create the `GestureRecognizer` to add and observe 68 | - returns: a `ControlEvent` that re-emit the gesture recognizer itself 69 | */ 70 | public func gesture(_ factory: Factory) -> ControlEvent { 71 | self.gesture(factory.gesture) 72 | } 73 | 74 | /** 75 | Reactive wrapper for a single view gesture recognizer. 76 | It automatically attaches the gesture recognizer to the receiver view. 77 | The value the `Observable` emits is the gesture recognizer itself. 78 | 79 | rx.gesture can't error and is subscribed/observed on main scheduler. 80 | - parameter gesture: a `GestureRecognizer` you want to add and observe 81 | - returns: a `ControlEvent` that re-emit the gesture recognizer itself 82 | */ 83 | public func gesture(_ gesture: G) -> ControlEvent { 84 | 85 | let source = Observable.deferred { [weak control = self.base] () -> Observable in 86 | MainScheduler.ensureExecutingOnScheduler() 87 | 88 | guard let control = control else { return .empty() } 89 | 90 | let genericGesture = gesture as RxGestureRecognizer 91 | 92 | #if os(iOS) 93 | control.isUserInteractionEnabled = true 94 | #endif 95 | 96 | control.addGestureRecognizer(gesture) 97 | 98 | return genericGesture.rx.event 99 | .compactMap { $0 as? G } 100 | .startWith(gesture) 101 | .do(onDispose: { [weak control, weak gesture] () in 102 | guard let gesture = gesture else { return } 103 | DispatchQueue.main.async { 104 | control?.removeGestureRecognizer(gesture) 105 | } 106 | }) 107 | .take(until: control.rx.deallocated) 108 | } 109 | 110 | return ControlEvent(events: source) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Pod/Classes/iOS/ForceTouchGestureRecognizer.swift: -------------------------------------------------------------------------------- 1 | #if canImport(UIKit) 2 | 3 | import UIKit.UIGestureRecognizerSubclass 4 | import RxSwift 5 | import RxCocoa 6 | 7 | public class ForceTouchGestureRecognizer: UIGestureRecognizer { 8 | 9 | private var touch: UITouch? 10 | public var force: CGFloat { 11 | touch?.force ?? 0 12 | } 13 | 14 | public var maximumPossibleForce: CGFloat { 15 | touch?.maximumPossibleForce ?? 0 16 | } 17 | 18 | public var absoluteFractionCompleted: CGFloat { 19 | guard maximumPossibleForce > 0 else { 20 | return 0 21 | } 22 | return force / maximumPossibleForce 23 | } 24 | 25 | public var minimumFractionCompletedRequired: CGFloat = 0 26 | public var maximumFractionCompletedRequired: CGFloat = 1 27 | 28 | public var fractionCompleted: CGFloat { 29 | lerp( 30 | mapMin: minimumFractionCompletedRequired, to: 0, 31 | mapMax: maximumFractionCompletedRequired, to: 1, 32 | value: absoluteFractionCompleted 33 | ) 34 | } 35 | 36 | public override func touchesBegan(_ touches: Set, with event: UIEvent) { 37 | super.touchesBegan(touches, with: event) 38 | 39 | guard state == .possible else { return } 40 | guard touch == nil else { return } 41 | guard let first = touches.first(where: { $0.phase == .began }) else { return } 42 | touch = first 43 | state = .began 44 | } 45 | 46 | public override func touchesMoved(_ touches: Set, with event: UIEvent) { 47 | super.touchesMoved(touches, with: event) 48 | guard let touch = touch, touches.contains(touch), touch.phase == .moved else { return } 49 | state = .changed 50 | } 51 | 52 | public override func touchesEnded(_ touches: Set, with event: UIEvent) { 53 | super.touchesEnded(touches, with: event) 54 | guard let touch = touch, touches.contains(touch), touch.phase == .ended else { return } 55 | self.touch = nil 56 | state = .ended 57 | } 58 | 59 | public override func touchesCancelled(_ touches: Set, with event: UIEvent) { 60 | super.touchesCancelled(touches, with: event) 61 | guard let touch = touch, touches.contains(touch), touch.phase == .cancelled else { return } 62 | self.touch = nil 63 | state = .cancelled 64 | } 65 | } 66 | 67 | public typealias ForceTouchConfiguration = Configuration 68 | public typealias ForceTouchControlEvent = ControlEvent 69 | public typealias ForceTouchObservable = Observable 70 | 71 | extension Factory where Gesture == RxGestureRecognizer { 72 | 73 | /** 74 | Returns an `AnyFactory` for `ForceTouchGestureRecognizer` 75 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 76 | */ 77 | public static func forceTouch(configuration: ForceTouchConfiguration? = nil) -> AnyFactory { 78 | make(configuration: configuration).abstracted() 79 | } 80 | } 81 | 82 | extension Reactive where Base: RxGestureView { 83 | 84 | /** 85 | Returns an observable `ForceTouchGestureRecognizer` events sequence 86 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 87 | */ 88 | public func forceTouchGesture(configuration: ForceTouchConfiguration? = nil) -> ForceTouchControlEvent { 89 | gesture(make(configuration: configuration)) 90 | } 91 | } 92 | 93 | extension ObservableType where Element: ForceTouchGestureRecognizer { 94 | 95 | /** 96 | Maps the observable `GestureRecognizer` events sequence to an observable sequence of force values. 97 | */ 98 | public func asForce() -> Observable { 99 | self.map { $0.force } 100 | } 101 | 102 | public func when(fractionCompletedExceeds threshold: CGFloat) -> Observable { 103 | let source = asObservable() 104 | return source 105 | .when(.began) 106 | .flatMapLatest { [unowned source] _ in 107 | source 108 | .when(.changed) 109 | .filter { 110 | if threshold == 0 { 111 | return $0.fractionCompleted > threshold 112 | } else { 113 | return $0.fractionCompleted >= threshold 114 | } 115 | } 116 | .take(1) 117 | } 118 | } 119 | } 120 | 121 | private func lerp(_ v0: T, _ v1: T, _ t: T) -> T { 122 | v0 + (v1 - v0) * t 123 | } 124 | 125 | private func lerp(mapMin: T, to min: T, mapMax: T, to max: T, value: T) -> T { 126 | lerp(min, max, (value - mapMin) / (mapMax - mapMin)) 127 | } 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /Pod/Classes/iOS/TouchDownGestureRecognizer.swift: -------------------------------------------------------------------------------- 1 | #if canImport(UIKit) 2 | 3 | import UIKit.UIGestureRecognizerSubclass 4 | import RxSwift 5 | import RxCocoa 6 | 7 | public class TouchDownGestureRecognizer: UIGestureRecognizer { 8 | 9 | public override init(target: Any?, action: Selector?) { 10 | super.init(target: target, action: action) 11 | 12 | trigger 13 | .flatMapFirst { [unowned self] _ -> Observable in 14 | let trigger = Observable.just(()) 15 | guard self.state == .possible else { 16 | return trigger 17 | } 18 | return trigger.delay( 19 | .milliseconds(Int(self.minimumTouchDuration * 1000)), 20 | scheduler: MainScheduler.instance 21 | ) 22 | } 23 | .subscribe(onNext: { [unowned self] _ in 24 | self.touches = self._touches 25 | }) 26 | .disposed(by: triggerDisposeBag) 27 | } 28 | 29 | public var minimumTouchDuration: TimeInterval = 0 30 | 31 | /** 32 | When set to `false`, it allows to bypass the touch ignoring mechanism in order to get absolutely all touch down events. 33 | Defaults to `true`. 34 | - note: See [ignore(_ touch: UITouch, for event: UIEvent)](https://developer.apple.com/documentation/uikit/uigesturerecognizer/1620010-ignore) 35 | */ 36 | public var isTouchIgnoringEnabled: Bool = true 37 | 38 | @nonobjc public var touches: Set = [] { 39 | didSet { 40 | if touches.isEmpty { 41 | if state == .possible { 42 | state = .cancelled 43 | } else { 44 | state = .ended 45 | } 46 | } else { 47 | if state == .possible { 48 | state = .began 49 | } else { 50 | state = .changed 51 | } 52 | } 53 | } 54 | } 55 | 56 | public override func touchesBegan(_ touches: Set, with event: UIEvent) { 57 | super.touchesBegan(touches, with: event) 58 | setTouches(from: event) 59 | } 60 | 61 | public override func touchesMoved(_ touches: Set, with event: UIEvent) { 62 | super.touchesMoved(touches, with: event) 63 | setTouches(from: event) 64 | } 65 | 66 | public override func touchesEnded(_ touches: Set, with event: UIEvent) { 67 | super.touchesEnded(touches, with: event) 68 | setTouches(from: event) 69 | } 70 | 71 | public override func touchesCancelled(_ touches: Set, with event: UIEvent) { 72 | super.touchesCancelled(touches, with: event) 73 | setTouches(from: event) 74 | } 75 | 76 | private let triggerDisposeBag = DisposeBag() 77 | private let trigger = PublishSubject() 78 | private var _touches: Set = [] 79 | private func setTouches(from event: UIEvent) { 80 | _touches = (event.allTouches ?? []).filter { touch in 81 | [.began, .stationary, .moved].contains(touch.phase) 82 | } 83 | trigger.onNext(()) 84 | } 85 | 86 | public override func reset() { 87 | super.reset() 88 | touches = [] 89 | } 90 | 91 | public override func ignore(_ touch: UITouch, for event: UIEvent) { 92 | guard isTouchIgnoringEnabled else { 93 | return 94 | } 95 | super.ignore(touch, for: event) 96 | } 97 | 98 | } 99 | 100 | public typealias TouchDownConfiguration = Configuration 101 | public typealias TouchDownControlEvent = ControlEvent 102 | public typealias TouchDownObservable = Observable 103 | 104 | extension Factory where Gesture == RxGestureRecognizer { 105 | 106 | /** 107 | Returns an `AnyFactory` for `TouchDownGestureRecognizer` 108 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 109 | */ 110 | public static func touchDown(configuration: TouchDownConfiguration? = nil) -> AnyFactory { 111 | make(configuration: configuration).abstracted() 112 | } 113 | } 114 | 115 | extension Reactive where Base: RxGestureView { 116 | 117 | /** 118 | Returns an observable `TouchDownGestureRecognizer` events sequence 119 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 120 | */ 121 | public func touchDownGesture(configuration: TouchDownConfiguration? = nil) -> TouchDownControlEvent { 122 | gesture(make(configuration: configuration)) 123 | } 124 | } 125 | 126 | extension ObservableType where Element: TouchDownGestureRecognizer { 127 | 128 | /** 129 | Maps the observable `GestureRecognizer` events sequence to an observable sequence of force values. 130 | */ 131 | public func asTouches() -> Observable> { 132 | self.map { $0.touches } 133 | } 134 | } 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /Pod/Classes/iOS/TransformGestureRecognizers.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #if canImport(UIKit) 22 | 23 | import UIKit 24 | import RxSwift 25 | import RxCocoa 26 | 27 | public struct TransformGestureRecognizers { 28 | public let panGesture: UIPanGestureRecognizer 29 | public let rotationGesture: UIRotationGestureRecognizer 30 | public let pinchGesture: UIPinchGestureRecognizer 31 | } 32 | 33 | public struct TransformVelocity { 34 | let translation: CGPoint 35 | let rotation: CGFloat 36 | let scale: CGFloat 37 | } 38 | 39 | public typealias TransformControlEvent = ControlEvent 40 | public typealias TransformObservable = Observable 41 | 42 | extension Reactive where Base: RxGestureView { 43 | public func transformGestures() -> TransformControlEvent { 44 | let source = Observable.combineLatest(panGesture(), rotationGesture(), pinchGesture()) { 45 | TransformGestureRecognizers( 46 | panGesture: $0, 47 | rotationGesture: $1, 48 | pinchGesture: $2 49 | ) 50 | } 51 | return ControlEvent(events: source) 52 | } 53 | } 54 | 55 | extension ObservableType where Element == TransformGestureRecognizers { 56 | 57 | public func when(_ states: RxGestureRecognizerState...) -> Observable { 58 | filter { gestures in 59 | states.contains(gestures.panGesture.state) 60 | || states.contains(gestures.rotationGesture.state) 61 | || states.contains(gestures.pinchGesture.state) 62 | } 63 | } 64 | 65 | public func asTransform(in view: TargetView = .view) -> Observable<(transform: CGAffineTransform, velocity: TransformVelocity)> { 66 | map { gestures in 67 | let translationView = view.targetView(for: gestures.panGesture) 68 | let translation = gestures.panGesture.translation(in: translationView) 69 | 70 | let transform = CGAffineTransform.identity 71 | .rotated(by: gestures.rotationGesture.rotation) 72 | .scaledBy(x: gestures.pinchGesture.scale, y: gestures.pinchGesture.scale) 73 | .translatedBy(x: translation.x, y: translation.y) 74 | 75 | let velocity = TransformVelocity( 76 | translation: gestures.panGesture.velocity(in: translationView), 77 | rotation: gestures.rotationGesture.velocity, 78 | scale: gestures.pinchGesture.velocity 79 | ) 80 | 81 | return (transform, velocity) 82 | } 83 | } 84 | } 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /Pod/Classes/iOS/UIHoverGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #if canImport(UIKit) 22 | 23 | import UIKit 24 | import RxSwift 25 | import RxCocoa 26 | 27 | 28 | @available(iOS 13.0, *) 29 | public typealias HoverConfiguration = Configuration 30 | @available(iOS 13.0, *) 31 | public typealias HoverControlEvent = ControlEvent 32 | @available(iOS 13.0, *) 33 | public typealias HoverObservable = Observable 34 | 35 | 36 | @available(iOS 13.0, *) 37 | extension Factory where Gesture == RxGestureRecognizer { 38 | 39 | /** 40 | Returns an `AnyFactory` for `UIHoverGestureRecognizer` 41 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 42 | */ 43 | public static func hover(configuration: HoverConfiguration? = nil) -> AnyFactory { 44 | make(configuration: configuration).abstracted() 45 | } 46 | } 47 | 48 | @available(iOS 13.0, *) 49 | extension Reactive where Base: RxGestureView { 50 | 51 | /** 52 | Returns an observable `UIHoverGestureRecognizer` events sequence 53 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 54 | */ 55 | public func hoverGesture(configuration: HoverConfiguration? = nil) -> HoverControlEvent { 56 | gesture(make(configuration: configuration)) 57 | } 58 | } 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /Pod/Classes/iOS/UILongPressGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #if canImport(UIKit) 22 | 23 | import UIKit 24 | import RxSwift 25 | import RxCocoa 26 | 27 | public typealias LongPressConfiguration = Configuration 28 | public typealias LongPressControlEvent = ControlEvent 29 | public typealias LongPressObservable = Observable 30 | 31 | extension Factory where Gesture == RxGestureRecognizer { 32 | 33 | /** 34 | Returns an `AnyFactory` for `UILongPressGestureRecognizer` 35 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 36 | */ 37 | public static func longPress(configuration: LongPressConfiguration? = nil) -> AnyFactory { 38 | make(configuration: configuration).abstracted() 39 | } 40 | } 41 | 42 | extension Reactive where Base: RxGestureView { 43 | 44 | /** 45 | Returns an observable `UILongPressGestureRecognizer` events sequence 46 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 47 | */ 48 | public func longPressGesture(configuration: LongPressConfiguration? = nil) -> LongPressControlEvent { 49 | gesture(make(configuration: configuration)) 50 | } 51 | } 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /Pod/Classes/iOS/UIPanGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #if canImport(UIKit) 22 | 23 | import UIKit 24 | import RxSwift 25 | import RxCocoa 26 | 27 | public typealias PanConfiguration = Configuration 28 | public typealias PanControlEvent = ControlEvent 29 | public typealias PanObservable = Observable 30 | 31 | extension Factory where Gesture == RxGestureRecognizer { 32 | 33 | /** 34 | Returns an `AnyFactory` for `UIPanGestureRecognizer` 35 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 36 | */ 37 | public static func pan(configuration: PanConfiguration? = nil) -> AnyFactory { 38 | make(configuration: configuration).abstracted() 39 | } 40 | } 41 | 42 | extension Reactive where Base: RxGestureView { 43 | 44 | /** 45 | Returns an observable `UIPanGestureRecognizer` events sequence 46 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 47 | */ 48 | public func panGesture(configuration: PanConfiguration? = nil) -> PanControlEvent { 49 | gesture(make(configuration: configuration)) 50 | } 51 | } 52 | 53 | extension ObservableType where Element: UIPanGestureRecognizer { 54 | 55 | /** 56 | Maps the observable `GestureRecognizer` events sequence to an observable sequence of translation values of the pan gesture in the coordinate system of the specified `view` alongside the gesture velocity. 57 | 58 | - parameter view: A `TargetView` value on which the gesture took place. 59 | */ 60 | public func asTranslation(in view: TargetView = .view) -> Observable<(translation: CGPoint, velocity: CGPoint)> { 61 | self.map { gesture in 62 | let view = view.targetView(for: gesture) 63 | return ( 64 | gesture.translation(in: view), 65 | gesture.velocity(in: view) 66 | ) 67 | } 68 | } 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /Pod/Classes/iOS/UIPinchGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #if canImport(UIKit) 22 | 23 | import UIKit 24 | import RxSwift 25 | import RxCocoa 26 | 27 | public typealias PinchConfiguration = Configuration 28 | public typealias PinchControlEvent = ControlEvent 29 | public typealias PinchObservable = Observable 30 | 31 | extension Factory where Gesture == RxGestureRecognizer { 32 | 33 | /** 34 | Returns an `AnyFactory` for `UIPinchGestureRecognizer` 35 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 36 | */ 37 | public static func pinch(configuration: PinchConfiguration? = nil) -> AnyFactory { 38 | make(configuration: configuration).abstracted() 39 | } 40 | } 41 | 42 | extension Reactive where Base: RxGestureView { 43 | 44 | /** 45 | Returns an observable `UIPinchGestureRecognizer` events sequence 46 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 47 | */ 48 | public func pinchGesture(configuration: PinchConfiguration? = nil) -> PinchControlEvent { 49 | gesture(make(configuration: configuration)) 50 | } 51 | } 52 | 53 | extension ObservableType where Element: UIPinchGestureRecognizer { 54 | 55 | /** 56 | Maps the observable `GestureRecognizer` events sequence to an observable sequence of scale factors relative to the points of the two touches in screen coordinates alongside the gesture velocity. 57 | */ 58 | public func asScale() -> Observable<(scale: CGFloat, velocity: CGFloat)> { 59 | self.map { gesture in 60 | (gesture.scale, gesture.velocity) 61 | } 62 | } 63 | } 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /Pod/Classes/iOS/UIRotationGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #if canImport(UIKit) 22 | 23 | import UIKit 24 | import RxSwift 25 | import RxCocoa 26 | 27 | public typealias RotationConfiguration = Configuration 28 | public typealias RotationControlEvent = ControlEvent 29 | public typealias RotationObservable = Observable 30 | 31 | extension Factory where Gesture == RxGestureRecognizer { 32 | 33 | /** 34 | Returns an `AnyFactory` for `UIRotationGestureRecognizer` 35 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 36 | */ 37 | public static func rotation(configuration: RotationConfiguration? = nil) -> AnyFactory { 38 | make(configuration: configuration).abstracted() 39 | } 40 | } 41 | 42 | extension Reactive where Base: RxGestureView { 43 | 44 | /** 45 | Returns an observable `UIRotationGestureRecognizer` events sequence 46 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 47 | */ 48 | public func rotationGesture(configuration: RotationConfiguration? = nil) -> RotationControlEvent { 49 | gesture(make(configuration: configuration)) 50 | } 51 | } 52 | 53 | extension ObservableType where Element: UIRotationGestureRecognizer { 54 | 55 | /** 56 | Maps the observable `GestureRecognizer` events sequence to an observable sequence of rotation values of the gesture in radians alongside the gesture velocity. 57 | */ 58 | public func asRotation() -> Observable<(rotation: CGFloat, velocity: CGFloat)> { 59 | self.map { gesture in 60 | (gesture.rotation, gesture.velocity) 61 | } 62 | } 63 | } 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /Pod/Classes/iOS/UIScreenEdgePanGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #if canImport(UIKit) 22 | 23 | import UIKit 24 | import RxSwift 25 | import RxCocoa 26 | 27 | public typealias ScreenEdgePanConfiguration = Configuration 28 | public typealias ScreenEdgePanControlEvent = ControlEvent 29 | public typealias ScreenEdgePanObservable = Observable 30 | 31 | extension Factory where Gesture == RxGestureRecognizer { 32 | 33 | /** 34 | Returns an `AnyFactory` for `UIScreenEdgePanGestureRecognizer` 35 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 36 | */ 37 | public static func screenEdgePan(configuration: ScreenEdgePanConfiguration? = nil) -> AnyFactory { 38 | make(configuration: configuration).abstracted() 39 | } 40 | } 41 | 42 | extension Reactive where Base: RxGestureView { 43 | 44 | /** 45 | Returns an observable `UIScreenEdgePanGestureRecognizer` events sequence 46 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 47 | */ 48 | public func screenEdgePanGesture(configuration: ScreenEdgePanConfiguration? = nil) -> ScreenEdgePanControlEvent { 49 | gesture(make(configuration: configuration)) 50 | } 51 | } 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /Pod/Classes/iOS/UISwipeGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #if canImport(UIKit) 22 | 23 | import UIKit 24 | import RxSwift 25 | import RxCocoa 26 | 27 | public enum SwipeDirection { 28 | case right, left, up, down 29 | 30 | fileprivate typealias SwipeGestureRecognizerDirection = UISwipeGestureRecognizer.Direction 31 | 32 | fileprivate var direction: SwipeGestureRecognizerDirection { 33 | switch self { 34 | case .right: return .right 35 | case .left: return .left 36 | case .up: return .up 37 | case .down: return .down 38 | } 39 | } 40 | } 41 | 42 | private func make(direction: SwipeDirection, configuration: Configuration?) -> Factory { 43 | make { 44 | $0.direction = direction.direction 45 | configuration?($0, $1) 46 | } 47 | } 48 | 49 | public typealias SwipeConfiguration = Configuration 50 | public typealias SwipeControlEvent = ControlEvent 51 | public typealias SwipeObservable = Observable 52 | 53 | extension Factory where Gesture == RxGestureRecognizer { 54 | 55 | /** 56 | Returns an `AnyFactory` for `UISwipeGestureRecognizer` 57 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 58 | */ 59 | public static func swipe(direction: SwipeDirection, configuration: SwipeConfiguration? = nil) -> AnyFactory { 60 | make(direction: direction, configuration: configuration).abstracted() 61 | } 62 | } 63 | 64 | extension Reactive where Base: RxGestureView { 65 | 66 | /** 67 | Returns an observable `UISwipeGestureRecognizer` events sequence 68 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 69 | */ 70 | private func swipeGesture(direction: SwipeDirection,configuration: SwipeConfiguration? = nil) -> SwipeControlEvent { 71 | gesture(make(direction: direction, configuration: configuration)) 72 | } 73 | 74 | /** 75 | Returns an observable `UISwipeGestureRecognizer` events sequence 76 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 77 | */ 78 | public func swipeGesture(_ directions: Set,configuration: SwipeConfiguration? = nil) -> SwipeControlEvent { 79 | let source = Observable.merge(directions.map { 80 | swipeGesture(direction: $0, configuration: configuration).asObservable() 81 | }) 82 | return ControlEvent(events: source) 83 | } 84 | 85 | /** 86 | Returns an observable `UISwipeGestureRecognizer` events sequence 87 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 88 | */ 89 | public func swipeGesture(_ directions: SwipeDirection...,configuration: SwipeConfiguration? = nil) -> SwipeControlEvent { 90 | swipeGesture(Set(directions), configuration: configuration) 91 | } 92 | 93 | } 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /Pod/Classes/iOS/UITapGestureRecognizer+RxGesture.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) RxSwiftCommunity 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #if canImport(UIKit) 22 | 23 | import UIKit 24 | import RxSwift 25 | import RxCocoa 26 | 27 | public typealias TapConfiguration = Configuration 28 | public typealias TapControlEvent = ControlEvent 29 | public typealias TapObservable = Observable 30 | 31 | extension Factory where Gesture == RxGestureRecognizer { 32 | 33 | /** 34 | Returns an `AnyFactory` for `UITapGestureRecognizer` 35 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 36 | */ 37 | public static func tap(configuration: TapConfiguration? = nil) -> AnyFactory { 38 | make(configuration: configuration).abstracted() 39 | } 40 | } 41 | 42 | extension Reactive where Base: RxGestureView { 43 | 44 | /** 45 | Returns an observable `UITapGestureRecognizer` events sequence 46 | - parameter configuration: A closure that allows to fully configure the gesture recognizer 47 | */ 48 | public func tapGesture(configuration: TapConfiguration? = nil) -> TapControlEvent { 49 | gesture(make(configuration: configuration)) 50 | } 51 | } 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxGesture 2 | 3 | [![Version](https://img.shields.io/cocoapods/v/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture) 4 | [![License](https://img.shields.io/cocoapods/l/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture) 5 | [![Platform](https://img.shields.io/cocoapods/p/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture) 6 | 7 | ## Usage 8 | 9 | ![](Pod/Assets/demo.gif) 10 | 11 | To run the example project, clone the repo, in the __Example__ folder open `RxGesture.xcworkspace`. 12 | 13 | You _might_ need to run `pod install` from the Example directory first. 14 | 15 | --- 16 | 17 | __RxGesture__ allows you to easily turn any view into a tappable or swipeable control like so: 18 | 19 | ```swift 20 | view.rx 21 | .tapGesture() 22 | .when(.recognized) 23 | .subscribe(onNext: { _ in 24 | //react to taps 25 | }) 26 | .disposed(by: stepBag) 27 | ``` 28 | 29 | You can also react to more than one gesture. For example to dismiss a photo preview you might want to do that when the user taps it, or swipes up or down: 30 | 31 | ```swift 32 | view.rx 33 | .anyGesture(.tap(), .swipe([.up, .down])) 34 | .when(.recognized) 35 | .subscribe(onNext: { _ in 36 | //dismiss presented photo 37 | }) 38 | .disposed(by: stepBag) 39 | ``` 40 | 41 | `rx.gesture` is defined as `Observable` where `G` is the actual type of the gesture recognizer so what it emits is the gesture recognizer itself (handy if want to call methods like `asLocation(in view:)` or `asTranslation(in view:)`) 42 | 43 | 44 | #### On iOS, RxGesture supports: 45 | 46 | ```swift 47 | view.rx.tapGesture() -> ControlEvent 48 | view.rx.pinchGesture() -> ControlEvent 49 | view.rx.swipeGesture(.left) -> ControlEvent 50 | view.rx.panGesture() -> ControlEvent 51 | view.rx.longPressGesture() -> ControlEvent 52 | view.rx.rotationGesture() -> ControlEvent 53 | view.rx.screenEdgePanGesture() -> ControlEvent 54 | view.rx.hoverGesture() -> ControlEvent 55 | 56 | view.rx.anyGesture(.tap(), ...) -> ControlEvent 57 | view.rx.anyGesture(.pinch(), ...) -> ControlEvent 58 | view.rx.anyGesture(.swipe(.left), ...) -> ControlEvent 59 | view.rx.anyGesture(.pan(), ...) -> ControlEvent 60 | view.rx.anyGesture(.longPress(), ...) -> ControlEvent 61 | view.rx.anyGesture(.rotation(), ...) -> ControlEvent 62 | view.rx.anyGesture(.screenEdgePan(), ...) -> ControlEvent 63 | view.rx.anyGesture(.hover(), ...) -> ControlEvent 64 | ``` 65 | 66 | #### On macOS, RxGesture supports: 67 | 68 | ```swift 69 | view.rx.clickGesture() -> ControlEvent 70 | view.rx.rightClickGesture() -> ControlEvent 71 | view.rx.panGesture() -> ControlEvent 72 | view.rx.pressGesture() -> ControlEvent 73 | view.rx.rotationGesture() -> ControlEvent 74 | view.rx.magnificationGesture() -> ControlEvent 75 | 76 | view.rx.anyGesture(.click(), ...) -> ControlEvent 77 | view.rx.anyGesture(.rightClick(), ...) -> ControlEvent 78 | view.rx.anyGesture(.pan(), ...) -> ControlEvent 79 | view.rx.anyGesture(.press(), ...) -> ControlEvent 80 | view.rx.anyGesture(.rotation(), ...) -> ControlEvent 81 | view.rx.anyGesture(.magnification(), ...) -> ControlEvent 82 | ``` 83 | 84 | 85 | ℹ️ If you use a gesture recognizer alone, prefer the `view.rx.fooGesture()` syntax over `view.rx.anyGesture(.foo())` because it returns the concrete `UIGestureRecognizer` subclass and avoid you to cast it in `subscribe()`. 86 | 87 | 88 | ## Filtering State 89 | 90 | By default, there is no filter on the state of the gesture recognizer. That means that you will always receive a first event with the initial state of the gesture recognizer (almost always `.possible`). 91 | 92 | Here are the preferred states that can be used for each kind of gestures (__iOS__ and __macOS__): 93 | 94 | Kind | States 95 | ---|--- 96 | `.tap()` `.click()` `.rightClick()` `.swipe()`| `.recognized` 97 | `.longPress()` `.press()` | `.began` 98 | `.pan()` `.pinch()` `.rotation()` `.magnification()` `.screenEdgePan()` | `.began` `.changed` `.ended` 99 | 100 | You usually filter the state using the `.when()` operator: 101 | ```swift 102 | view.rx.tapGesture().when(.recognized) 103 | view.rx.panGesture().when(.began, .changed, .ended) 104 | ``` 105 | 106 | If you are observing multiple gestures at once, you can use the `.when()` operator if you want to filter against the same state for __all__ gesture recognizers, or use the tuple syntax for individual filtering: 107 | 108 | ```swift 109 | view.rx 110 | .anyGesture(.tap(), .swipe([.up, .down])) 111 | .when(.recognized) 112 | .subscribe(onNext: { gesture in 113 | // Called whenever a tap, a swipe-up or a swipe-down is recognized (state == .recognized) 114 | }) 115 | .disposed(by: bag) 116 | 117 | view.rx 118 | .anyGesture( 119 | (.tap(), when: .recognized), 120 | (.pan(), when: .ended) 121 | ) 122 | .subscribe(onNext: { gesture in 123 | // Called whenever: 124 | // - a tap is recognized (state == .recognized) 125 | // - or a pan is ended (state == .ended) 126 | }) 127 | .disposed(by: bag) 128 | ``` 129 | 130 | 131 | __The demo app includes examples for all recognizers ➡️ [iOS](Example/RxGesture/ViewController.swift), [macOS](Example/RxGesture-OSX/ViewController.swift)__. 132 | 133 | ## Delegate customization 134 | ### Lightweight customization 135 | Each gesture recognizer has a default `RxGestureRecognizerDelegate`. It allows you to customize every delegate method using a policy: 136 | - `.always` will return `true` to the corresponding delegate method 137 | - `.never` will return `false` to the corresponding delegate method 138 | - `.custom` takes an associated closure that will be executed to return a value to the corresponding delegate method 139 | 140 | Here are the available policies with their corresponding delegate method: 141 | ```swift 142 | beginPolicy -> gestureRecognizerShouldBegin(:_) 143 | touchReceptionPolicy -> gestureRecognizer(_:shouldReceive:) 144 | selfFailureRequirementPolicy -> gestureRecognizer(_:shouldBeRequiredToFailBy:) 145 | otherFailureRequirementPolicy -> gestureRecognizer(_:shouldRequireFailureOf:) 146 | simultaneousRecognitionPolicy -> gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) 147 | eventRecognitionAttemptPolicy -> gestureRecognizer(_:shouldAttemptToRecognizeWith:) // macOS only 148 | pressReceptionPolicy -> gestureRecognizer(_:shouldReceive:) // iOS only 149 | ``` 150 | 151 | This delegate can be customized in the configuration closure: 152 | ```swift 153 | view.rx.tapGesture(configuration: { gestureRecognizer, delegate in 154 | delegate.simultaneousRecognitionPolicy = .always // (default value) 155 | // or 156 | delegate.simultaneousRecognitionPolicy = .never 157 | // or 158 | delegate.simultaneousRecognitionPolicy = .custom { gestureRecognizer, otherGestureRecognizer in 159 | return otherGestureRecognizer is UIPanGestureRecognizer 160 | } 161 | delegate.otherFailureRequirementPolicy = .custom { gestureRecognizer, otherGestureRecognizer in 162 | return otherGestureRecognizer is UILongPressGestureRecognizer 163 | } 164 | }) 165 | ``` 166 | 167 | Default values can be found in [`RxGestureRecognizerDelegate.swift`](Pod/Classes/RxGestureRecognizerDelegate.swift#L56). 168 | 169 | ### Full customization 170 | You can also replace the default delegate by your own, or remove it. 171 | ```swift 172 | view.rx.tapGesture { [unowned self] gestureRecognizer, delegate in 173 | gestureRecognizer.delegate = nil 174 | // or 175 | gestureRecognizer.delegate = self 176 | } 177 | ``` 178 | 179 | ## Requirements 180 | 181 | This library depends on both __RxSwift__ and __RxCocoa__. 182 | 183 | 184 | ## Installation 185 | 186 | ### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html) 187 | 188 | Add this to `Podfile` 189 | 190 | ```swift 191 | pod "RxGesture" 192 | ``` 193 | 194 | ```bash 195 | $ pod install 196 | ``` 197 | 198 | ### [Carthage](https://github.com/Carthage/Carthage) 199 | 200 | Add this to `Cartfile` 201 | 202 | ``` 203 | github "RxSwiftCommunity/RxGesture" ~> 3.0 204 | ``` 205 | 206 | ```bash 207 | $ carthage update 208 | ``` 209 | 210 | ## Thanks 211 | 212 | Everyone in the RxSwift Slack channel 💯 213 | 214 | ## License 215 | 216 | RxGesture is available under the MIT license. See the LICENSE file for more info. 217 | -------------------------------------------------------------------------------- /RxGesture.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint RxGesture.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | 6 | Pod::Spec.new do |s| 7 | s.name = "RxGesture" 8 | s.version = "4.0.4" 9 | s.summary = "RxSwift reactive wrapper for view gestures." 10 | s.swift_version = "5.1" 11 | 12 | s.description = <<-DESC 13 | RxSwift reactive wrapper for view gestures. It lets you to easily observe 14 | a single gesture like tap or a custom group of gestures on a view. You can 15 | combine taps, presses, or swipes in any direction 16 | DESC 17 | 18 | s.homepage = "https://github.com/RxSwiftCommunity/RxGesture" 19 | s.license = 'MIT' 20 | s.authors = { "RxSwiftCommunity" => "https://github.com/RxSwiftCommunity", "Jérôme Alves" => "j.alves@me.com" } 21 | s.source = { :git => "https://github.com/RxSwiftCommunity/RxGesture.git", :tag => s.version.to_s } 22 | 23 | s.requires_arc = true 24 | 25 | s.ios.deployment_target = '9.0' 26 | s.osx.deployment_target = '10.10' 27 | 28 | s.source_files = 'Pod/Classes/*.swift' 29 | 30 | s.ios.source_files = 'Pod/Classes/iOS/*.swift' 31 | s.osx.source_files = 'Pod/Classes/OSX/*.swift' 32 | 33 | s.dependency 'RxSwift', '~> 6.0' 34 | s.dependency 'RxCocoa', '~> 6.0' 35 | 36 | end 37 | -------------------------------------------------------------------------------- /RxGesture/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | opt_in_rules: 2 | - overridden_super_call 3 | - private_outlet 4 | - prohibited_super_call 5 | - first_where 6 | - closure_spacing 7 | - unneeded_parentheses_in_closure_argument 8 | - redundant_nil_coalescing 9 | - pattern_matching_keywords 10 | - explicit_init 11 | - contains_over_first_not_nil 12 | - implicit_return 13 | 14 | disabled_rules: 15 | - line_length 16 | - trailing_whitespace 17 | - type_name 18 | - identifier_name 19 | - vertical_whitespace 20 | - trailing_newline 21 | - opening_brace 22 | - large_tuple 23 | - file_length 24 | - comma 25 | - colon 26 | - private_over_fileprivate 27 | - force_cast 28 | - force_try 29 | - function_parameter_count 30 | - statement_position 31 | - legacy_hashing 32 | - todo 33 | - operator_whitespace 34 | - type_body_length 35 | - function_body_length 36 | - cyclomatic_complexity 37 | 38 | included: 39 | - ../Pod/Classes -------------------------------------------------------------------------------- /RxGesture/RxGesture copy-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /RxGesture/RxGesture-iOS-Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /RxGesture/RxGesture-iOS-Tests/RxGestureTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utilities.swift 3 | // RxGesture-iOS-Tests 4 | // 5 | // Created by Jérôme Alves on 05/11/2017. 6 | // Copyright © 2017 RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | import RxSwift 12 | import RxCocoa 13 | import RxTest 14 | import RxBlocking 15 | @testable import RxGesture 16 | 17 | class RxGestureTests: XCTestCase { 18 | 19 | var disposeBag: DisposeBag! 20 | var view: UIView! 21 | var gesture: UIGestureRecognizer! 22 | 23 | override func setUp() { 24 | super.setUp() 25 | disposeBag = DisposeBag() 26 | view = UIView() 27 | gesture = UIGestureRecognizer() 28 | } 29 | 30 | override func tearDown() { 31 | super.tearDown() 32 | disposeBag = nil 33 | view = nil 34 | gesture = nil 35 | } 36 | 37 | func testObservable() { 38 | 39 | let states: [UIGestureRecognizer.State] = [.began, .changed, .changed, .changed, .ended] 40 | 41 | send(states) 42 | 43 | let expected = [.possible] + states 44 | 45 | let result = try? view.rx.gesture(gesture) 46 | .map { $0.state } 47 | .take(expected.count) 48 | .toBlocking(timeout: 1) 49 | .toArray() 50 | 51 | XCTAssertEqual(result ?? [], expected) 52 | } 53 | 54 | func send(_ states: [UIGestureRecognizer.State]) { 55 | guard let first = states.first else { 56 | return 57 | } 58 | DispatchQueue.main.async { 59 | self.gesture.state = first 60 | self.send(Array(states[1...])) 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /RxGesture/RxGesture.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RxGesture/RxGesture.xcodeproj/xcshareddata/xcschemes/RxGesture-OSX.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /RxGesture/RxGesture.xcodeproj/xcshareddata/xcschemes/RxGesture-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 79 | 85 | 86 | 87 | 88 | 89 | 90 | 96 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /RxGesture/RxGesture/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /RxGesture/RxGesture/RxGesture.h: -------------------------------------------------------------------------------- 1 | // 2 | // RxGesture.h 3 | // RxGesture 4 | // 5 | // Created by James Perlman on 11/27/16. 6 | // Copyright © 2016 AutoLotto. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for RxGesture. 12 | FOUNDATION_EXPORT double RxGestureVersionNumber; 13 | 14 | //! Project version string for RxGesture. 15 | FOUNDATION_EXPORT const unsigned char RxGestureVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | if ! command -v carthage > /dev/null; then 4 | printf 'Carthage is not installed.\n' 5 | printf 'See https://github.com/Carthage/Carthage for install instructions.\n' 6 | exit 1 7 | fi 8 | 9 | carthage update --use-xcframeworks --use-submodules --no-use-binaries 10 | --------------------------------------------------------------------------------