You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
7.9 KiB

  1. # RxGesture
  2. [![Version](https://img.shields.io/cocoapods/v/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture)
  3. [![License](https://img.shields.io/cocoapods/l/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture)
  4. [![Platform](https://img.shields.io/cocoapods/p/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture)
  5. ## Usage
  6. ![](Pod/Assets/demo.gif)
  7. To run the example project, clone the repo, in the __Example__ folder open `RxGesture.xcworkspace`.
  8. You _might_ need to run `pod install` from the Example directory first.
  9. ---
  10. __RxGesture__ allows you to easily turn any view into a tappable or swipeable control like so:
  11. ```swift
  12. view.rx
  13. .tapGesture()
  14. .when(.recognized)
  15. .subscribe(onNext: { _ in
  16. //react to taps
  17. })
  18. .disposed(by: stepBag)
  19. ```
  20. 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:
  21. ```swift
  22. view.rx
  23. .anyGesture(.tap(), .swipe([.up, .down]))
  24. .when(.recognized)
  25. .subscribe(onNext: { _ in
  26. //dismiss presented photo
  27. })
  28. .disposed(by: stepBag)
  29. ```
  30. `rx.gesture` is defined as `Observable<G>` 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:)`)
  31. #### On iOS, RxGesture supports:
  32. ```swift
  33. view.rx.tapGesture() -> ControlEvent<UITapGestureRecognizer>
  34. view.rx.pinchGesture() -> ControlEvent<UIPinchGestureRecognizer>
  35. view.rx.swipeGesture(.left) -> ControlEvent<UISwipeGestureRecognizer>
  36. view.rx.panGesture() -> ControlEvent<UIPanGestureRecognizer>
  37. view.rx.longPressGesture() -> ControlEvent<UILongPressGestureRecognizer>
  38. view.rx.rotationGesture() -> ControlEvent<UIRotationGestureRecognizer>
  39. view.rx.screenEdgePanGesture() -> ControlEvent<UIScreenEdgePanGestureRecognizer>
  40. view.rx.hoverGesture() -> ControlEvent<UIHoverGestureRecognizer>
  41. view.rx.anyGesture(.tap(), ...) -> ControlEvent<UIGestureRecognizer>
  42. view.rx.anyGesture(.pinch(), ...) -> ControlEvent<UIGestureRecognizer>
  43. view.rx.anyGesture(.swipe(.left), ...) -> ControlEvent<UIGestureRecognizer>
  44. view.rx.anyGesture(.pan(), ...) -> ControlEvent<UIGestureRecognizer>
  45. view.rx.anyGesture(.longPress(), ...) -> ControlEvent<UIGestureRecognizer>
  46. view.rx.anyGesture(.rotation(), ...) -> ControlEvent<UIGestureRecognizer>
  47. view.rx.anyGesture(.screenEdgePan(), ...) -> ControlEvent<UIGestureRecognizer>
  48. view.rx.anyGesture(.hover(), ...) -> ControlEvent<UIGestureRecognizer>
  49. ```
  50. #### On macOS, RxGesture supports:
  51. ```swift
  52. view.rx.clickGesture() -> ControlEvent<NSClickGestureRecognizer>
  53. view.rx.rightClickGesture() -> ControlEvent<NSClickGestureRecognizer>
  54. view.rx.panGesture() -> ControlEvent<NSPanGestureRecognizer>
  55. view.rx.pressGesture() -> ControlEvent<NSPressGestureRecognizer>
  56. view.rx.rotationGesture() -> ControlEvent<NSRotationGestureRecognizer>
  57. view.rx.magnificationGesture() -> ControlEvent<NSMagnificationGestureRecognizer>
  58. view.rx.anyGesture(.click(), ...) -> ControlEvent<NSGestureRecognizer>
  59. view.rx.anyGesture(.rightClick(), ...) -> ControlEvent<NSGestureRecognizer>
  60. view.rx.anyGesture(.pan(), ...) -> ControlEvent<NSGestureRecognizer>
  61. view.rx.anyGesture(.press(), ...) -> ControlEvent<NSGestureRecognizer>
  62. view.rx.anyGesture(.rotation(), ...) -> ControlEvent<NSGestureRecognizer>
  63. view.rx.anyGesture(.magnification(), ...) -> ControlEvent<NSGestureRecognizer>
  64. ```
  65. ℹ️ 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()`.
  66. ## Filtering State
  67. 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`).
  68. Here are the preferred states that can be used for each kind of gestures (__iOS__ and __macOS__):
  69. Kind | States
  70. ---|---
  71. `.tap()` `.click()` `.rightClick()` `.swipe()`| `.recognized`
  72. `.longPress()` `.press()` | `.began`
  73. `.pan()` `.pinch()` `.rotation()` `.magnification()` `.screenEdgePan()` | `.began` `.changed` `.ended`
  74. You usually filter the state using the `.when()` operator:
  75. ```swift
  76. view.rx.tapGesture().when(.recognized)
  77. view.rx.panGesture().when(.began, .changed, .ended)
  78. ```
  79. 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:
  80. ```swift
  81. view.rx
  82. .anyGesture(.tap(), .swipe([.up, .down]))
  83. .when(.recognized)
  84. .subscribe(onNext: { gesture in
  85. // Called whenever a tap, a swipe-up or a swipe-down is recognized (state == .recognized)
  86. })
  87. .disposed(by: bag)
  88. view.rx
  89. .anyGesture(
  90. (.tap(), when: .recognized),
  91. (.pan(), when: .ended)
  92. )
  93. .subscribe(onNext: { gesture in
  94. // Called whenever:
  95. // - a tap is recognized (state == .recognized)
  96. // - or a pan is ended (state == .ended)
  97. })
  98. .disposed(by: bag)
  99. ```
  100. __The demo app includes examples for all recognizers ➡️ [iOS](Example/RxGesture/ViewController.swift), [macOS](Example/RxGesture-OSX/ViewController.swift)__.
  101. ## Delegate customization
  102. ### Lightweight customization
  103. Each gesture recognizer has a default `RxGestureRecognizerDelegate`. It allows you to customize every delegate method using a policy:
  104. - `.always` will return `true` to the corresponding delegate method
  105. - `.never` will return `false` to the corresponding delegate method
  106. - `.custom` takes an associated closure that will be executed to return a value to the corresponding delegate method
  107. Here are the available policies with their corresponding delegate method:
  108. ```swift
  109. beginPolicy -> gestureRecognizerShouldBegin(:_)
  110. touchReceptionPolicy -> gestureRecognizer(_:shouldReceive:)
  111. selfFailureRequirementPolicy -> gestureRecognizer(_:shouldBeRequiredToFailBy:)
  112. otherFailureRequirementPolicy -> gestureRecognizer(_:shouldRequireFailureOf:)
  113. simultaneousRecognitionPolicy -> gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)
  114. eventRecognitionAttemptPolicy -> gestureRecognizer(_:shouldAttemptToRecognizeWith:) // macOS only
  115. pressReceptionPolicy -> gestureRecognizer(_:shouldReceive:) // iOS only
  116. ```
  117. This delegate can be customized in the configuration closure:
  118. ```swift
  119. view.rx.tapGesture(configuration: { gestureRecognizer, delegate in
  120. delegate.simultaneousRecognitionPolicy = .always // (default value)
  121. // or
  122. delegate.simultaneousRecognitionPolicy = .never
  123. // or
  124. delegate.simultaneousRecognitionPolicy = .custom { gestureRecognizer, otherGestureRecognizer in
  125. return otherGestureRecognizer is UIPanGestureRecognizer
  126. }
  127. delegate.otherFailureRequirementPolicy = .custom { gestureRecognizer, otherGestureRecognizer in
  128. return otherGestureRecognizer is UILongPressGestureRecognizer
  129. }
  130. })
  131. ```
  132. Default values can be found in [`RxGestureRecognizerDelegate.swift`](Pod/Classes/RxGestureRecognizerDelegate.swift#L56).
  133. ### Full customization
  134. You can also replace the default delegate by your own, or remove it.
  135. ```swift
  136. view.rx.tapGesture { [unowned self] gestureRecognizer, delegate in
  137. gestureRecognizer.delegate = nil
  138. // or
  139. gestureRecognizer.delegate = self
  140. }
  141. ```
  142. ## Requirements
  143. This library depends on both __RxSwift__ and __RxCocoa__.
  144. ## Installation
  145. ### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html)
  146. Add this to `Podfile`
  147. ```swift
  148. pod "RxGesture"
  149. ```
  150. ```bash
  151. $ pod install
  152. ```
  153. ### [Carthage](https://github.com/Carthage/Carthage)
  154. Add this to `Cartfile`
  155. ```
  156. github "RxSwiftCommunity/RxGesture" ~> 3.0
  157. ```
  158. ```bash
  159. $ carthage update
  160. ```
  161. ## Thanks
  162. Everyone in the RxSwift Slack channel 💯
  163. ## License
  164. RxGesture is available under the MIT license. See the LICENSE file for more info.