{"id":19685933,"url":"https://github.com/rxswiftcommunity/rxgesture","last_synced_at":"2025-05-15T07:03:51.875Z","repository":{"id":37735651,"uuid":"54464241","full_name":"RxSwiftCommunity/RxGesture","owner":"RxSwiftCommunity","description":"RxSwift reactive wrapper for view gestures","archived":false,"fork":false,"pushed_at":"2022-08-12T04:07:09.000Z","size":2348,"stargazers_count":1396,"open_issues_count":6,"forks_count":183,"subscribers_count":31,"default_branch":"main","last_synced_at":"2025-05-15T07:03:48.661Z","etag":null,"topics":["click","gesture","pan","recognizers","rxswift","swipe"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RxSwiftCommunity.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-22T09:54:11.000Z","updated_at":"2025-05-12T15:59:43.000Z","dependencies_parsed_at":"2022-07-12T15:18:23.350Z","dependency_job_id":null,"html_url":"https://github.com/RxSwiftCommunity/RxGesture","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RxSwiftCommunity%2FRxGesture","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RxSwiftCommunity%2FRxGesture/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RxSwiftCommunity%2FRxGesture/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RxSwiftCommunity%2FRxGesture/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RxSwiftCommunity","download_url":"https://codeload.github.com/RxSwiftCommunity/RxGesture/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254291974,"owners_count":22046425,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["click","gesture","pan","recognizers","rxswift","swipe"],"created_at":"2024-11-11T18:24:48.884Z","updated_at":"2025-05-15T07:03:51.846Z","avatar_url":"https://github.com/RxSwiftCommunity.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RxGesture\n\n[![Version](https://img.shields.io/cocoapods/v/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture)\n[![License](https://img.shields.io/cocoapods/l/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture)\n[![Platform](https://img.shields.io/cocoapods/p/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture)\n\n## Usage\n\n![](Pod/Assets/demo.gif)\n\nTo run the example project, clone the repo, in the __Example__ folder open `RxGesture.xcworkspace`.\n\nYou _might_ need to run `pod install` from the Example directory first.\n\n---\n\n__RxGesture__ allows you to easily turn any view into a tappable or swipeable control like so:\n\n```swift\nview.rx\n  .tapGesture()\n  .when(.recognized)\n  .subscribe(onNext: { _ in\n    //react to taps\n  })\n  .disposed(by: stepBag)\n```\n\nYou 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:\n\n```swift\nview.rx\n  .anyGesture(.tap(), .swipe([.up, .down]))\n  .when(.recognized)\n  .subscribe(onNext: { _ in\n    //dismiss presented photo\n  })\n  .disposed(by: stepBag)\n```\n\n`rx.gesture` is defined as `Observable\u003cG\u003e` 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:)`)\n\n\n#### On iOS, RxGesture supports:\n\n```swift\nview.rx.tapGesture()           -\u003e ControlEvent\u003cUITapGestureRecognizer\u003e\nview.rx.pinchGesture()         -\u003e ControlEvent\u003cUIPinchGestureRecognizer\u003e\nview.rx.swipeGesture(.left)    -\u003e ControlEvent\u003cUISwipeGestureRecognizer\u003e\nview.rx.panGesture()           -\u003e ControlEvent\u003cUIPanGestureRecognizer\u003e\nview.rx.longPressGesture()     -\u003e ControlEvent\u003cUILongPressGestureRecognizer\u003e\nview.rx.rotationGesture()      -\u003e ControlEvent\u003cUIRotationGestureRecognizer\u003e\nview.rx.screenEdgePanGesture() -\u003e ControlEvent\u003cUIScreenEdgePanGestureRecognizer\u003e\nview.rx.hoverGesture()         -\u003e ControlEvent\u003cUIHoverGestureRecognizer\u003e\n\nview.rx.anyGesture(.tap(), ...)           -\u003e ControlEvent\u003cUIGestureRecognizer\u003e\nview.rx.anyGesture(.pinch(), ...)         -\u003e ControlEvent\u003cUIGestureRecognizer\u003e\nview.rx.anyGesture(.swipe(.left), ...)    -\u003e ControlEvent\u003cUIGestureRecognizer\u003e\nview.rx.anyGesture(.pan(), ...)           -\u003e ControlEvent\u003cUIGestureRecognizer\u003e\nview.rx.anyGesture(.longPress(), ...)     -\u003e ControlEvent\u003cUIGestureRecognizer\u003e\nview.rx.anyGesture(.rotation(), ...)      -\u003e ControlEvent\u003cUIGestureRecognizer\u003e\nview.rx.anyGesture(.screenEdgePan(), ...) -\u003e ControlEvent\u003cUIGestureRecognizer\u003e\nview.rx.anyGesture(.hover(), ...)         -\u003e ControlEvent\u003cUIGestureRecognizer\u003e\n```\n\n#### On macOS, RxGesture supports:\n\n```swift\nview.rx.clickGesture()         -\u003e ControlEvent\u003cNSClickGestureRecognizer\u003e\nview.rx.rightClickGesture()    -\u003e ControlEvent\u003cNSClickGestureRecognizer\u003e\nview.rx.panGesture()           -\u003e ControlEvent\u003cNSPanGestureRecognizer\u003e\nview.rx.pressGesture()         -\u003e ControlEvent\u003cNSPressGestureRecognizer\u003e\nview.rx.rotationGesture()      -\u003e ControlEvent\u003cNSRotationGestureRecognizer\u003e\nview.rx.magnificationGesture() -\u003e ControlEvent\u003cNSMagnificationGestureRecognizer\u003e\n\nview.rx.anyGesture(.click(), ...)         -\u003e ControlEvent\u003cNSGestureRecognizer\u003e\nview.rx.anyGesture(.rightClick(), ...)    -\u003e ControlEvent\u003cNSGestureRecognizer\u003e\nview.rx.anyGesture(.pan(), ...)           -\u003e ControlEvent\u003cNSGestureRecognizer\u003e\nview.rx.anyGesture(.press(), ...)         -\u003e ControlEvent\u003cNSGestureRecognizer\u003e\nview.rx.anyGesture(.rotation(), ...)      -\u003e ControlEvent\u003cNSGestureRecognizer\u003e\nview.rx.anyGesture(.magnification(), ...) -\u003e ControlEvent\u003cNSGestureRecognizer\u003e\n```\n\n\nℹ️ 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()`.\n\n\n## Filtering State\n\nBy 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`).\n\nHere are the preferred states that can be used for each kind of gestures (__iOS__ and __macOS__):\n\nKind | States\n---|---\n`.tap()` `.click()` `.rightClick()` `.swipe()`| `.recognized`\n`.longPress()` `.press()` | `.began`\n`.pan()` `.pinch()` `.rotation()` `.magnification()` `.screenEdgePan()` | `.began` `.changed` `.ended`\n\nYou usually filter the state using the `.when()` operator:\n```swift\nview.rx.tapGesture().when(.recognized)\nview.rx.panGesture().when(.began, .changed, .ended)\n```\n\nIf 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:\n\n```swift\nview.rx\n  .anyGesture(.tap(), .swipe([.up, .down]))\n  .when(.recognized)\n  .subscribe(onNext: { gesture in\n    // Called whenever a tap, a swipe-up or a swipe-down is recognized (state == .recognized)\n  })\n  .disposed(by: bag)\n\nview.rx\n  .anyGesture(\n    (.tap(), when: .recognized),\n    (.pan(), when: .ended)\n  )\n  .subscribe(onNext: { gesture in\n    // Called whenever:\n    // - a tap is recognized (state == .recognized)\n    // - or a pan is ended (state == .ended)\n  })\n  .disposed(by: bag)\n```\n\n\n__The demo app includes examples for all recognizers ➡️ [iOS](Example/RxGesture/ViewController.swift), [macOS](Example/RxGesture-OSX/ViewController.swift)__.\n\n## Delegate customization\n### Lightweight customization\nEach gesture recognizer has a default `RxGestureRecognizerDelegate`. It allows you to customize every delegate method using a policy:\n- `.always` will return `true` to the corresponding delegate method\n- `.never` will return `false` to the corresponding delegate method\n- `.custom` takes an associated closure that will be executed to return a value to the corresponding delegate method\n\nHere are the available policies with their corresponding delegate method:\n```swift\nbeginPolicy                   -\u003e gestureRecognizerShouldBegin(:_)\ntouchReceptionPolicy          -\u003e gestureRecognizer(_:shouldReceive:)\nselfFailureRequirementPolicy  -\u003e gestureRecognizer(_:shouldBeRequiredToFailBy:)\notherFailureRequirementPolicy -\u003e gestureRecognizer(_:shouldRequireFailureOf:)\nsimultaneousRecognitionPolicy -\u003e gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)\neventRecognitionAttemptPolicy -\u003e gestureRecognizer(_:shouldAttemptToRecognizeWith:) // macOS only\npressReceptionPolicy          -\u003e gestureRecognizer(_:shouldReceive:) // iOS only\n```\n\nThis delegate can be customized in the configuration closure:\n```swift\nview.rx.tapGesture(configuration: { gestureRecognizer, delegate in\n  delegate.simultaneousRecognitionPolicy = .always // (default value)\n  // or\n  delegate.simultaneousRecognitionPolicy = .never\n  // or\n  delegate.simultaneousRecognitionPolicy = .custom { gestureRecognizer, otherGestureRecognizer in\n    return otherGestureRecognizer is UIPanGestureRecognizer\n  }\n  delegate.otherFailureRequirementPolicy = .custom { gestureRecognizer, otherGestureRecognizer in\n    return otherGestureRecognizer is UILongPressGestureRecognizer\n  }\n})\n```\n\nDefault values can be found in [`RxGestureRecognizerDelegate.swift`](Pod/Classes/RxGestureRecognizerDelegate.swift#L56).\n\n### Full customization\nYou can also replace the default delegate by your own, or remove it.\n```swift\nview.rx.tapGesture { [unowned self] gestureRecognizer, delegate in\n  gestureRecognizer.delegate = nil\n  // or\n  gestureRecognizer.delegate = self\n}\n```\n\n## Requirements\n\nThis library depends on both __RxSwift__ and __RxCocoa__.\n\n\n## Installation\n\n### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html)\n\nAdd this to `Podfile`\n\n```swift\npod \"RxGesture\"\n```\n\n```bash\n$ pod install\n```\n\n### [Carthage](https://github.com/Carthage/Carthage)\n\nAdd this to `Cartfile`\n\n```\ngithub \"RxSwiftCommunity/RxGesture\" ~\u003e 3.0\n```\n\n```bash\n$ carthage update\n```\n\n## Thanks\n\nEveryone in the RxSwift Slack channel 💯\n\n## License\n\nRxGesture is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frxswiftcommunity%2Frxgesture","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frxswiftcommunity%2Frxgesture","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frxswiftcommunity%2Frxgesture/lists"}