Dibya Malla
2 years ago
54 changed files with 14999 additions and 12504 deletions
-
BIN.DS_Store
-
32GME Remit.xcodeproj/project.pbxproj
-
8GME Remit.xcodeproj/xcshareddata/IDETemplateMacros.plist
-
8GME Remit.xcworkspace/xcshareddata/IDETemplateMacros.plist
-
39GME Remit/Extensions/UIView+Ext.swift
-
BINGME Remit/Models/.DS_Store
-
68GME Remit/Modules/Main/User Interface/View/MainViewController.swift
-
92GME Remit/Utilities/TabBar Helper/CustomItemTabView.swift
-
96GME Remit/Utilities/TabBar Helper/CustomTabBar.swift
-
67GME Remit/Utilities/TabBar Helper/CustomTabItem.swift
-
1Podfile
-
8Podfile.lock
-
8Pods/Manifest.lock
-
25181Pods/Pods.xcodeproj/project.pbxproj
-
19Pods/RxGesture/LICENSE
-
173Pods/RxGesture/Pod/Classes/GenericRxGestureRecognizerDelegate.swift
-
60Pods/RxGesture/Pod/Classes/GestureFactory.swift
-
74Pods/RxGesture/Pod/Classes/GestureRecognizer+RxGesture.swift
-
70Pods/RxGesture/Pod/Classes/SharedTypes.swift
-
112Pods/RxGesture/Pod/Classes/View+RxGesture.swift
-
129Pods/RxGesture/Pod/Classes/iOS/ForceTouchGestureRecognizer.swift
-
136Pods/RxGesture/Pod/Classes/iOS/TouchDownGestureRecognizer.swift
-
86Pods/RxGesture/Pod/Classes/iOS/TransformGestureRecognizers.swift
-
60Pods/RxGesture/Pod/Classes/iOS/UIHoverGestureRecognizer+RxGesture.swift
-
53Pods/RxGesture/Pod/Classes/iOS/UILongPressGestureRecognizer+RxGesture.swift
-
71Pods/RxGesture/Pod/Classes/iOS/UIPanGestureRecognizer+RxGesture.swift
-
65Pods/RxGesture/Pod/Classes/iOS/UIPinchGestureRecognizer+RxGesture.swift
-
65Pods/RxGesture/Pod/Classes/iOS/UIRotationGestureRecognizer+RxGesture.swift
-
53Pods/RxGesture/Pod/Classes/iOS/UIScreenEdgePanGestureRecognizer+RxGesture.swift
-
95Pods/RxGesture/Pod/Classes/iOS/UISwipeGestureRecognizer+RxGesture.swift
-
53Pods/RxGesture/Pod/Classes/iOS/UITapGestureRecognizer+RxGesture.swift
-
216Pods/RxGesture/README.md
-
23Pods/Target Support Files/Pods-GME Remit/Pods-GME Remit-acknowledgements.markdown
-
29Pods/Target Support Files/Pods-GME Remit/Pods-GME Remit-acknowledgements.plist
-
2Pods/Target Support Files/Pods-GME Remit/Pods-GME Remit-frameworks.sh
-
8Pods/Target Support Files/Pods-GME Remit/Pods-GME Remit.debug.xcconfig
-
8Pods/Target Support Files/Pods-GME Remit/Pods-GME Remit.release.xcconfig
-
23Pods/Target Support Files/Pods-GMERemittanceTests/Pods-GMERemittanceTests-acknowledgements.markdown
-
29Pods/Target Support Files/Pods-GMERemittanceTests/Pods-GMERemittanceTests-acknowledgements.plist
-
2Pods/Target Support Files/Pods-GMERemittanceTests/Pods-GMERemittanceTests-frameworks.sh
-
8Pods/Target Support Files/Pods-GMERemittanceTests/Pods-GMERemittanceTests.debug.xcconfig
-
8Pods/Target Support Files/Pods-GMERemittanceTests/Pods-GMERemittanceTests.release.xcconfig
-
23Pods/Target Support Files/Pods-GMERemittanceUITests/Pods-GMERemittanceUITests-acknowledgements.markdown
-
29Pods/Target Support Files/Pods-GMERemittanceUITests/Pods-GMERemittanceUITests-acknowledgements.plist
-
2Pods/Target Support Files/Pods-GMERemittanceUITests/Pods-GMERemittanceUITests-frameworks.sh
-
8Pods/Target Support Files/Pods-GMERemittanceUITests/Pods-GMERemittanceUITests.debug.xcconfig
-
8Pods/Target Support Files/Pods-GMERemittanceUITests/Pods-GMERemittanceUITests.release.xcconfig
-
26Pods/Target Support Files/RxGesture/RxGesture-Info.plist
-
5Pods/Target Support Files/RxGesture/RxGesture-dummy.m
-
12Pods/Target Support Files/RxGesture/RxGesture-prefix.pch
-
16Pods/Target Support Files/RxGesture/RxGesture-umbrella.h
-
15Pods/Target Support Files/RxGesture/RxGesture.debug.xcconfig
-
6Pods/Target Support Files/RxGesture/RxGesture.modulemap
-
15Pods/Target Support Files/RxGesture/RxGesture.release.xcconfig
@ -0,0 +1,8 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
|
<plist version="1.0"> |
||||
|
<dict> |
||||
|
<key>FILEHEADER</key> |
||||
|
<string></string> |
||||
|
</dict> |
||||
|
</plist> |
@ -0,0 +1,8 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
|
<plist version="1.0"> |
||||
|
<dict> |
||||
|
<key>FILEHEADER</key> |
||||
|
<string></string> |
||||
|
</dict> |
||||
|
</plist> |
@ -0,0 +1,92 @@ |
|||||
|
// |
||||
|
// CustomItemTabView.swift |
||||
|
// GME Remit |
||||
|
// |
||||
|
// Created by Dibya Malla Thakuri on 04/03/2023. |
||||
|
// Copyright © 2023 Gobal Money Express Co. Ltd. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
import Foundation |
||||
|
import UIKit |
||||
|
|
||||
|
final class CustomItemTabView: UIView { |
||||
|
private let iconImageView = UIImageView() |
||||
|
private let underlineView = UIView() |
||||
|
private let containerView = UIView() |
||||
|
let index: Int |
||||
|
|
||||
|
var isSelected = false { |
||||
|
didSet { |
||||
|
animateItems() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private let item: CustomTabItem |
||||
|
|
||||
|
init(with item: CustomTabItem, index: Int) { |
||||
|
self.item = item |
||||
|
self.index = index |
||||
|
|
||||
|
super.init(frame: .zero) |
||||
|
|
||||
|
setupHierarchy() |
||||
|
setupLayout() |
||||
|
setupProperties() |
||||
|
} |
||||
|
|
||||
|
required init?(coder: NSCoder) { |
||||
|
fatalError("init(coder:) has not been implemented") |
||||
|
} |
||||
|
|
||||
|
private func setupHierarchy() { |
||||
|
|
||||
|
[ containerView, |
||||
|
iconImageView, |
||||
|
underlineView ].forEach({ |
||||
|
$0.translatesAutoresizingMaskIntoConstraints = false |
||||
|
}) |
||||
|
|
||||
|
addSubview(containerView) |
||||
|
containerView |
||||
|
.addSubviews(underlineView, iconImageView) |
||||
|
} |
||||
|
|
||||
|
private func setupLayout() { |
||||
|
|
||||
|
containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true |
||||
|
containerView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true |
||||
|
containerView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true |
||||
|
containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true |
||||
|
containerView.heightAnchor.constraint(equalToConstant: 80).isActive = true |
||||
|
|
||||
|
|
||||
|
|
||||
|
iconImageView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true |
||||
|
iconImageView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true |
||||
|
iconImageView.heightAnchor.constraint(equalToConstant: 40).isActive = true |
||||
|
|
||||
|
underlineView.leadingAnchor.constraint(equalTo: self.iconImageView.leadingAnchor).isActive = true |
||||
|
underlineView.trailingAnchor.constraint(equalTo: self.iconImageView.trailingAnchor).isActive = true |
||||
|
underlineView.heightAnchor.constraint(equalToConstant: 2).isActive = true |
||||
|
underlineView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true |
||||
|
} |
||||
|
|
||||
|
private func setupProperties() { |
||||
|
underlineView.backgroundColor = .blue |
||||
|
underlineView.set(cornerRadius: 2) |
||||
|
|
||||
|
iconImageView.image = isSelected ? item.selectedIcon : item.icon |
||||
|
} |
||||
|
|
||||
|
private func animateItems() { |
||||
|
UIView.animate(withDuration: 0.4) { [unowned self] in |
||||
|
|
||||
|
self.underlineView.alpha = self.isSelected ? 1.0 : 0.0 |
||||
|
} |
||||
|
UIView.transition(with: iconImageView, |
||||
|
duration: 0.4, |
||||
|
options: .transitionCrossDissolve) { [unowned self] in |
||||
|
self.iconImageView.image = self.isSelected ? self.item.selectedIcon : self.item.icon |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,96 @@ |
|||||
|
// |
||||
|
// CustomTabBar.swift |
||||
|
// GME Remit |
||||
|
// |
||||
|
// Created by Dibya Malla Thakuri on 05/03/2023. |
||||
|
// Copyright © 2023 Gobal Money Express Co. Ltd. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
import UIKit |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
import RxGesture |
||||
|
|
||||
|
final class CustomTabBar: UIStackView { |
||||
|
|
||||
|
var itemTapped: Observable<Int> { itemTappedSubject.asObservable() } |
||||
|
|
||||
|
private lazy var customItemViews: [CustomItemTabView] = [homeItem, sendItem, profileItem] |
||||
|
|
||||
|
private let homeItem = CustomItemTabView(with: .home, index: 0) |
||||
|
private let sendItem = CustomItemTabView(with: .sendMoney, index: 1) |
||||
|
private let profileItem = CustomItemTabView(with: .profile, index: 2) |
||||
|
private let itemTappedSubject = PublishSubject<Int>() |
||||
|
private let disposeBag = DisposeBag() |
||||
|
|
||||
|
init() { |
||||
|
super.init(frame: .zero) |
||||
|
|
||||
|
setupHierarchy() |
||||
|
setupProperties() |
||||
|
bind() |
||||
|
|
||||
|
setNeedsLayout() |
||||
|
layoutIfNeeded() |
||||
|
selectItem(index: 0) |
||||
|
} |
||||
|
|
||||
|
required init(coder: NSCoder) { |
||||
|
fatalError("init(coder:) has not been implemented") |
||||
|
} |
||||
|
|
||||
|
private func setupHierarchy() { |
||||
|
addArrangedSubviews([homeItem, sendItem, profileItem]) |
||||
|
} |
||||
|
|
||||
|
private func setupProperties() { |
||||
|
distribution = .fillEqually |
||||
|
alignment = .top |
||||
|
|
||||
|
backgroundColor = .themeWhite |
||||
|
setupCornerRadius(30) |
||||
|
|
||||
|
customItemViews.forEach { |
||||
|
$0.translatesAutoresizingMaskIntoConstraints = false |
||||
|
$0.clipsToBounds = true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private func selectItem(index: Int) { |
||||
|
customItemViews.forEach { $0.isSelected = $0.index == index } |
||||
|
itemTappedSubject.onNext(index) |
||||
|
} |
||||
|
|
||||
|
//MARK: - Bindings |
||||
|
private func bind() { |
||||
|
homeItem.rx.tapGesture() |
||||
|
.when(.recognized) |
||||
|
.bind { [weak self] _ in |
||||
|
guard let self = self else { return } |
||||
|
self.homeItem.animateClick { |
||||
|
self.selectItem(index: self.homeItem.index) |
||||
|
} |
||||
|
} |
||||
|
.disposed(by: disposeBag) |
||||
|
|
||||
|
sendItem.rx.tapGesture() |
||||
|
.when(.recognized) |
||||
|
.bind { [weak self] _ in |
||||
|
guard let self = self else { return } |
||||
|
self.sendItem.animateClick { |
||||
|
self.selectItem(index: self.sendItem.index) |
||||
|
} |
||||
|
} |
||||
|
.disposed(by: disposeBag) |
||||
|
|
||||
|
profileItem.rx.tapGesture() |
||||
|
.when(.recognized) |
||||
|
.bind { [weak self] _ in |
||||
|
guard let self = self else { return } |
||||
|
self.profileItem.animateClick { |
||||
|
self.selectItem(index: self.profileItem.index) |
||||
|
} |
||||
|
} |
||||
|
.disposed(by: disposeBag) |
||||
|
} |
||||
|
} |
@ -0,0 +1,67 @@ |
|||||
|
// |
||||
|
// CustomTabItem.swift |
||||
|
// GME Remit |
||||
|
// |
||||
|
// Created by Dibya Malla Thakuri on 04/03/2023. |
||||
|
// Copyright © 2023 Gobal Money Express Co. Ltd. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
import UIKit |
||||
|
|
||||
|
enum CustomTabItem: String, CaseIterable { |
||||
|
case home |
||||
|
case sendMoney |
||||
|
case profile |
||||
|
} |
||||
|
|
||||
|
extension CustomTabItem { |
||||
|
var viewController: UIViewController { |
||||
|
switch self { |
||||
|
case .home: |
||||
|
|
||||
|
let homeViewController = HomeWireframe().getMainView() |
||||
|
let navHomeViewController = SwipeNavigationController(rootViewController: homeViewController) |
||||
|
navHomeViewController.hero.isEnabled = true |
||||
|
|
||||
|
return navHomeViewController |
||||
|
case .sendMoney: |
||||
|
|
||||
|
let sendMoneyViewController = RecipientsWireframe().getMainView() |
||||
|
sendMoneyViewController.setupTabItem() |
||||
|
let navSendMoneyViewController = SwipeNavigationController(rootViewController: sendMoneyViewController) |
||||
|
navSendMoneyViewController.hero.isEnabled = true |
||||
|
return sendMoneyViewController |
||||
|
|
||||
|
case .profile: |
||||
|
let profileViewController = UIStoryboard(name: "Profile", bundle: nil) |
||||
|
.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController |
||||
|
|
||||
|
profileViewController.setupTabItem() |
||||
|
let navProfileViewController = SwipeNavigationController(rootViewController: profileViewController) |
||||
|
return navProfileViewController |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var icon: UIImage? { |
||||
|
switch self { |
||||
|
case .home: |
||||
|
return UIImage(named: "home_new")?.withRenderingMode(.alwaysOriginal) |
||||
|
case .sendMoney: |
||||
|
return UIImage(systemName: "heart.circle")?.withRenderingMode(.alwaysOriginal) |
||||
|
case .profile: |
||||
|
return UIImage(systemName: "person.crop.circle")?.withRenderingMode(.alwaysOriginal) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var selectedIcon: UIImage? { |
||||
|
switch self { |
||||
|
case .home: |
||||
|
return UIImage(named: "home_selected")?.withRenderingMode(.alwaysOriginal) |
||||
|
case .sendMoney: |
||||
|
return UIImage(systemName: "heart.circle.fill")?.withRenderingMode(.alwaysOriginal) |
||||
|
case .profile: |
||||
|
return UIImage(systemName: "person.crop.circle.fill")?.withRenderingMode(.alwaysOriginal) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
25181
Pods/Pods.xcodeproj/project.pbxproj
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in |
||||
|
all copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
THE SOFTWARE. |
@ -0,0 +1,173 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
#if os(iOS) |
||||
|
import UIKit |
||||
|
#elseif os(OSX) |
||||
|
import AppKit |
||||
|
#endif |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
public struct GestureRecognizerDelegatePolicy<PolicyInput> { |
||||
|
public typealias PolicyBody = (PolicyInput) -> Bool |
||||
|
|
||||
|
private let policy: PolicyBody |
||||
|
|
||||
|
private init(policy: @escaping PolicyBody) { |
||||
|
self.policy = policy |
||||
|
} |
||||
|
|
||||
|
public static func custom(_ policy: @escaping PolicyBody) |
||||
|
-> GestureRecognizerDelegatePolicy<PolicyInput> { |
||||
|
.init(policy: policy) |
||||
|
} |
||||
|
|
||||
|
public static var always: GestureRecognizerDelegatePolicy<PolicyInput> { |
||||
|
.init { _ in true } |
||||
|
} |
||||
|
|
||||
|
public static var never: GestureRecognizerDelegatePolicy<PolicyInput> { |
||||
|
.init { _ in false } |
||||
|
} |
||||
|
|
||||
|
public func isPolicyPassing(with args: PolicyInput) -> Bool { |
||||
|
policy(args) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func || <PolicyInput>(lhs: GestureRecognizerDelegatePolicy<PolicyInput>, rhs: GestureRecognizerDelegatePolicy<PolicyInput>) -> GestureRecognizerDelegatePolicy<PolicyInput> { |
||||
|
.custom { input in |
||||
|
lhs.isPolicyPassing(with: input) || rhs.isPolicyPassing(with: input) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func && <PolicyInput>(lhs: GestureRecognizerDelegatePolicy<PolicyInput>, rhs: GestureRecognizerDelegatePolicy<PolicyInput>) -> GestureRecognizerDelegatePolicy<PolicyInput> { |
||||
|
.custom { input in |
||||
|
lhs.isPolicyPassing(with: input) && rhs.isPolicyPassing(with: input) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public final class GenericRxGestureRecognizerDelegate<Gesture: RxGestureRecognizer>: NSObject, RxGestureRecognizerDelegate { |
||||
|
|
||||
|
/// Corresponding delegate method: gestureRecognizerShouldBegin(:_) |
||||
|
public var beginPolicy: GestureRecognizerDelegatePolicy<Gesture> = .always |
||||
|
|
||||
|
/// Corresponding delegate method: gestureRecognizer(_:shouldReceive:) |
||||
|
public var touchReceptionPolicy: GestureRecognizerDelegatePolicy<(Gesture, RxGestureTouch)> = .always |
||||
|
|
||||
|
/// Corresponding delegate method: gestureRecognizer(_:shouldBeRequiredToFailBy:) |
||||
|
public var selfFailureRequirementPolicy: GestureRecognizerDelegatePolicy<(Gesture, RxGestureRecognizer)> = .never |
||||
|
|
||||
|
/// Corresponding delegate method: gestureRecognizer(_:shouldRequireFailureOf:) |
||||
|
public var otherFailureRequirementPolicy: GestureRecognizerDelegatePolicy<(Gesture, RxGestureRecognizer)> = .never |
||||
|
|
||||
|
/// Corresponding delegate method: gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) |
||||
|
public var simultaneousRecognitionPolicy: GestureRecognizerDelegatePolicy<(Gesture, RxGestureRecognizer)> = .always |
||||
|
|
||||
|
#if os(iOS) |
||||
|
// Workaround because we can't have stored properties with @available annotation |
||||
|
private var _pressReceptionPolicy: Any? |
||||
|
|
||||
|
/// Corresponding delegate method: gestureRecognizer(_:shouldReceive:) |
||||
|
public var pressReceptionPolicy: GestureRecognizerDelegatePolicy<(Gesture, UIPress)> { |
||||
|
get { |
||||
|
_pressReceptionPolicy as? GestureRecognizerDelegatePolicy<(Gesture, UIPress)> ?? .always |
||||
|
} |
||||
|
set { |
||||
|
_pressReceptionPolicy = newValue |
||||
|
} |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
#if os(OSX) |
||||
|
/// Corresponding delegate method: gestureRecognizer(_:shouldAttemptToRecognizeWith:) |
||||
|
public var eventRecognitionAttemptPolicy: GestureRecognizerDelegatePolicy<(Gesture, NSEvent)> = .always |
||||
|
#endif |
||||
|
|
||||
|
public func gestureRecognizerShouldBegin( |
||||
|
_ gestureRecognizer: RxGestureRecognizer |
||||
|
) -> Bool { |
||||
|
beginPolicy.isPolicyPassing(with: gestureRecognizer as! Gesture) |
||||
|
} |
||||
|
|
||||
|
public func gestureRecognizer( |
||||
|
_ gestureRecognizer: RxGestureRecognizer, |
||||
|
shouldReceive touch: RxGestureTouch |
||||
|
) -> Bool { |
||||
|
touchReceptionPolicy.isPolicyPassing( |
||||
|
with: (gestureRecognizer as! Gesture, touch) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
public func gestureRecognizer( |
||||
|
_ gestureRecognizer: RxGestureRecognizer, |
||||
|
shouldRequireFailureOf otherGestureRecognizer: RxGestureRecognizer |
||||
|
) -> Bool { |
||||
|
otherFailureRequirementPolicy.isPolicyPassing( |
||||
|
with: (gestureRecognizer as! Gesture, otherGestureRecognizer) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
public func gestureRecognizer( |
||||
|
_ gestureRecognizer: RxGestureRecognizer, |
||||
|
shouldBeRequiredToFailBy otherGestureRecognizer: RxGestureRecognizer |
||||
|
) -> Bool { |
||||
|
selfFailureRequirementPolicy.isPolicyPassing( |
||||
|
with: (gestureRecognizer as! Gesture, otherGestureRecognizer) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
public func gestureRecognizer( |
||||
|
_ gestureRecognizer: RxGestureRecognizer, |
||||
|
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: RxGestureRecognizer |
||||
|
) -> Bool { |
||||
|
simultaneousRecognitionPolicy.isPolicyPassing( |
||||
|
with: (gestureRecognizer as! Gesture, otherGestureRecognizer) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
#if os(iOS) |
||||
|
|
||||
|
public func gestureRecognizer( |
||||
|
_ gestureRecognizer: RxGestureRecognizer, |
||||
|
shouldReceive press: UIPress |
||||
|
) -> Bool { |
||||
|
pressReceptionPolicy.isPolicyPassing( |
||||
|
with: (gestureRecognizer as! Gesture, press) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
#endif |
||||
|
|
||||
|
#if os(OSX) |
||||
|
|
||||
|
public func gestureRecognizer( |
||||
|
_ gestureRecognizer: RxGestureRecognizer, |
||||
|
shouldAttemptToRecognizeWith event: NSEvent |
||||
|
) -> Bool { |
||||
|
eventRecognitionAttemptPolicy.isPolicyPassing( |
||||
|
with: (gestureRecognizer as! Gesture, event) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
#endif |
||||
|
|
||||
|
} |
@ -0,0 +1,60 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
#if canImport(ObjectiveC) |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
import ObjectiveC |
||||
|
|
||||
|
public typealias Configuration<Gesture: RxGestureRecognizer> = (Gesture, GenericRxGestureRecognizerDelegate<Gesture>) -> Void |
||||
|
|
||||
|
public struct Factory<Gesture: RxGestureRecognizer> { |
||||
|
public let gesture: Gesture |
||||
|
public init(_ configuration: Configuration<Gesture>?) { |
||||
|
let gesture = Gesture() |
||||
|
let delegate = GenericRxGestureRecognizerDelegate<Gesture>() |
||||
|
objc_setAssociatedObject( |
||||
|
gesture, |
||||
|
&gestureRecognizerStrongDelegateKey, |
||||
|
delegate, |
||||
|
.OBJC_ASSOCIATION_RETAIN_NONATOMIC |
||||
|
) |
||||
|
gesture.delegate = delegate |
||||
|
configuration?(gesture, delegate) |
||||
|
self.gesture = gesture |
||||
|
} |
||||
|
|
||||
|
internal func abstracted() -> AnyFactory { |
||||
|
AnyFactory(self.gesture) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal func make<G>(configuration: Configuration<G>? = nil) -> Factory<G> { |
||||
|
Factory<G>(configuration) |
||||
|
} |
||||
|
|
||||
|
public typealias AnyFactory = Factory<RxGestureRecognizer> |
||||
|
extension Factory where Gesture == RxGestureRecognizer { |
||||
|
private init<G: RxGestureRecognizer>(_ gesture: G) { |
||||
|
self.gesture = gesture |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private var gestureRecognizerStrongDelegateKey: UInt8 = 0 |
||||
|
#endif |
@ -0,0 +1,74 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
import struct CoreGraphics.CGPoint |
||||
|
|
||||
|
public typealias LocationInView = (RxGestureView) -> CGPoint |
||||
|
|
||||
|
extension ObservableType where Element: RxGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Filters the observable `GestureRecognizer` events sequence based on the `GestureRecognizer` state. |
||||
|
|
||||
|
- parameter state: An `GestureRecognizerState` that is used to filter the `GestureRecognizer` events sequence. |
||||
|
- returns: An observable `GestureRecognizer` events sequence that only contains events emitted while the `GestureRecognizer`'s state match the given `state`. |
||||
|
*/ |
||||
|
public func when(_ states: RxGestureRecognizerState...) -> Observable<Element> { |
||||
|
filter { gesture in |
||||
|
states.contains(gesture.state) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
Filters the observable `GestureRecognizer` events sequence based on the `GestureRecognizer` state. |
||||
|
|
||||
|
- parameter state: An `GestureRecognizerState` that is used to filter the `GestureRecognizer` events sequence. |
||||
|
- returns: An observable `GestureRecognizer` events sequence that only contains events emitted while the `GestureRecognizer`'s state match the given `state`. |
||||
|
*/ |
||||
|
internal func when(_ states: [RxGestureRecognizerState]) -> Observable<Element> { |
||||
|
filter { gesture in |
||||
|
states.contains(gesture.state) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
Maps the observable `GestureRecognizer` events sequence to an observable sequence of points computed as the location in the given `view` of the gesture. |
||||
|
|
||||
|
- parameter view: A `TargetView` value on which the gesture took place. |
||||
|
*/ |
||||
|
public func asLocation(in view: TargetView = .view) -> Observable<RxGesturePoint> { |
||||
|
map { gesture in |
||||
|
gesture.location(in: view.targetView(for: gesture)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func asLocationInView() -> Observable<LocationInView> { |
||||
|
map { gesture in |
||||
|
let targetView = gesture.view! |
||||
|
let location = gesture.location(in: targetView) |
||||
|
return { view in |
||||
|
targetView.convert(location, to: view) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,70 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
import Foundation |
||||
|
|
||||
|
#if os(iOS) |
||||
|
import UIKit |
||||
|
public typealias RxGestureTouch = UITouch |
||||
|
public typealias RxGestureRecognizer = UIGestureRecognizer |
||||
|
public typealias RxGestureRecognizerState = UIGestureRecognizer.State |
||||
|
public typealias RxGestureRecognizerDelegate = UIGestureRecognizerDelegate |
||||
|
public typealias RxGestureView = UIView |
||||
|
public typealias RxGesturePoint = CGPoint |
||||
|
#elseif os(OSX) |
||||
|
import AppKit |
||||
|
public typealias RxGestureTouch = NSTouch |
||||
|
public typealias RxGestureRecognizer = NSGestureRecognizer |
||||
|
public typealias RxGestureRecognizerState = NSGestureRecognizer.State |
||||
|
public typealias RxGestureRecognizerDelegate = NSGestureRecognizerDelegate |
||||
|
public typealias RxGestureView = NSView |
||||
|
public typealias RxGesturePoint = NSPoint |
||||
|
#endif |
||||
|
|
||||
|
public enum TargetView { |
||||
|
/// The target view will be the gestureRecognizer's view |
||||
|
case view |
||||
|
|
||||
|
/// The target view will be the gestureRecognizer's view's superview |
||||
|
case superview |
||||
|
|
||||
|
/// The target view will be the gestureRecognizer's view's window |
||||
|
case window |
||||
|
|
||||
|
/// The target view will be the given view |
||||
|
case this(RxGestureView) |
||||
|
|
||||
|
public func targetView(for gestureRecognizer: RxGestureRecognizer) -> RxGestureView? { |
||||
|
switch self { |
||||
|
case .view: |
||||
|
return gestureRecognizer.view |
||||
|
case .superview: |
||||
|
return gestureRecognizer.view?.superview |
||||
|
case .window: |
||||
|
#if os(iOS) |
||||
|
return gestureRecognizer.view?.window |
||||
|
#elseif os(OSX) |
||||
|
return gestureRecognizer.view?.window?.contentView |
||||
|
#endif |
||||
|
case let .this(view): |
||||
|
return view |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,112 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
import Dispatch |
||||
|
|
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
|
||||
|
/** |
||||
|
Reactive wrapper for multiple view gesture recognizers. |
||||
|
It automatically attaches the gesture recognizers to the receiver view. |
||||
|
The value the `Observable` emits is the gesture recognizer itself. |
||||
|
|
||||
|
rx.anyGesture can't error and is subscribed/observed on main scheduler. |
||||
|
- parameter factories: a `(Factory + state)` collection you want to use to create the `GestureRecognizers` to add and observe |
||||
|
- returns: a `ControlEvent<G>` that re-emit the gesture recognizer itself |
||||
|
*/ |
||||
|
public func anyGesture(_ factories: (AnyFactory, when: RxGestureRecognizerState)...) -> ControlEvent<RxGestureRecognizer> { |
||||
|
let observables = factories.map { gesture, state in |
||||
|
self.gesture(gesture).when(state).asObservable() as Observable<RxGestureRecognizer> |
||||
|
} |
||||
|
let source = Observable.from(observables).merge() |
||||
|
return ControlEvent(events: source) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
Reactive wrapper for multiple view gesture recognizers. |
||||
|
It automatically attaches the gesture recognizers to the receiver view. |
||||
|
The value the `Observable` emits is the gesture recognizer itself. |
||||
|
|
||||
|
rx.anyGesture can't error and is subscribed/observed on main scheduler. |
||||
|
- parameter factories: a `Factory` collection you want to use to create the `GestureRecognizers` to add and observe |
||||
|
- returns: a `ControlEvent<G>` that re-emit the gesture recognizer itself |
||||
|
*/ |
||||
|
public func anyGesture(_ factories: AnyFactory...) -> ControlEvent<RxGestureRecognizer> { |
||||
|
let observables = factories.map { factory in |
||||
|
self.gesture(factory).asObservable() as Observable<RxGestureRecognizer> |
||||
|
} |
||||
|
let source = Observable.from(observables).merge() |
||||
|
return ControlEvent(events: source) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
Reactive wrapper for a single view gesture recognizer. |
||||
|
It automatically attaches the gesture recognizer to the receiver view. |
||||
|
The value the `Observable` emits is the gesture recognizer itself. |
||||
|
|
||||
|
rx.gesture can't error and is subscribed/observed on main scheduler. |
||||
|
- parameter factory: a `Factory` you want to use to create the `GestureRecognizer` to add and observe |
||||
|
- returns: a `ControlEvent<G>` that re-emit the gesture recognizer itself |
||||
|
*/ |
||||
|
public func gesture<G>(_ factory: Factory<G>) -> ControlEvent<G> { |
||||
|
self.gesture(factory.gesture) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
Reactive wrapper for a single view gesture recognizer. |
||||
|
It automatically attaches the gesture recognizer to the receiver view. |
||||
|
The value the `Observable` emits is the gesture recognizer itself. |
||||
|
|
||||
|
rx.gesture can't error and is subscribed/observed on main scheduler. |
||||
|
- parameter gesture: a `GestureRecognizer` you want to add and observe |
||||
|
- returns: a `ControlEvent<G>` that re-emit the gesture recognizer itself |
||||
|
*/ |
||||
|
public func gesture<G: RxGestureRecognizer>(_ gesture: G) -> ControlEvent<G> { |
||||
|
|
||||
|
let source = Observable.deferred { [weak control = self.base] () -> Observable<G> in |
||||
|
MainScheduler.ensureExecutingOnScheduler() |
||||
|
|
||||
|
guard let control = control else { return .empty() } |
||||
|
|
||||
|
let genericGesture = gesture as RxGestureRecognizer |
||||
|
|
||||
|
#if os(iOS) |
||||
|
control.isUserInteractionEnabled = true |
||||
|
#endif |
||||
|
|
||||
|
control.addGestureRecognizer(gesture) |
||||
|
|
||||
|
return genericGesture.rx.event |
||||
|
.compactMap { $0 as? G } |
||||
|
.startWith(gesture) |
||||
|
.do(onDispose: { [weak control, weak gesture] () in |
||||
|
guard let gesture = gesture else { return } |
||||
|
DispatchQueue.main.async { |
||||
|
control?.removeGestureRecognizer(gesture) |
||||
|
} |
||||
|
}) |
||||
|
.take(until: control.rx.deallocated) |
||||
|
} |
||||
|
|
||||
|
return ControlEvent(events: source) |
||||
|
} |
||||
|
} |
@ -0,0 +1,129 @@ |
|||||
|
#if canImport(UIKit) |
||||
|
|
||||
|
import UIKit.UIGestureRecognizerSubclass |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
public class ForceTouchGestureRecognizer: UIGestureRecognizer { |
||||
|
|
||||
|
private var touch: UITouch? |
||||
|
public var force: CGFloat { |
||||
|
touch?.force ?? 0 |
||||
|
} |
||||
|
|
||||
|
public var maximumPossibleForce: CGFloat { |
||||
|
touch?.maximumPossibleForce ?? 0 |
||||
|
} |
||||
|
|
||||
|
public var absoluteFractionCompleted: CGFloat { |
||||
|
guard maximumPossibleForce > 0 else { |
||||
|
return 0 |
||||
|
} |
||||
|
return force / maximumPossibleForce |
||||
|
} |
||||
|
|
||||
|
public var minimumFractionCompletedRequired: CGFloat = 0 |
||||
|
public var maximumFractionCompletedRequired: CGFloat = 1 |
||||
|
|
||||
|
public var fractionCompleted: CGFloat { |
||||
|
lerp( |
||||
|
mapMin: minimumFractionCompletedRequired, to: 0, |
||||
|
mapMax: maximumFractionCompletedRequired, to: 1, |
||||
|
value: absoluteFractionCompleted |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) { |
||||
|
super.touchesBegan(touches, with: event) |
||||
|
|
||||
|
guard state == .possible else { return } |
||||
|
guard touch == nil else { return } |
||||
|
guard let first = touches.first(where: { $0.phase == .began }) else { return } |
||||
|
touch = first |
||||
|
state = .began |
||||
|
} |
||||
|
|
||||
|
public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) { |
||||
|
super.touchesMoved(touches, with: event) |
||||
|
guard let touch = touch, touches.contains(touch), touch.phase == .moved else { return } |
||||
|
state = .changed |
||||
|
} |
||||
|
|
||||
|
public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) { |
||||
|
super.touchesEnded(touches, with: event) |
||||
|
guard let touch = touch, touches.contains(touch), touch.phase == .ended else { return } |
||||
|
self.touch = nil |
||||
|
state = .ended |
||||
|
} |
||||
|
|
||||
|
public override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) { |
||||
|
super.touchesCancelled(touches, with: event) |
||||
|
guard let touch = touch, touches.contains(touch), touch.phase == .cancelled else { return } |
||||
|
self.touch = nil |
||||
|
state = .cancelled |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public typealias ForceTouchConfiguration = Configuration<ForceTouchGestureRecognizer> |
||||
|
public typealias ForceTouchControlEvent = ControlEvent<ForceTouchGestureRecognizer> |
||||
|
public typealias ForceTouchObservable = Observable<ForceTouchGestureRecognizer> |
||||
|
|
||||
|
extension Factory where Gesture == RxGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Returns an `AnyFactory` for `ForceTouchGestureRecognizer` |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public static func forceTouch(configuration: ForceTouchConfiguration? = nil) -> AnyFactory { |
||||
|
make(configuration: configuration).abstracted() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `ForceTouchGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public func forceTouchGesture(configuration: ForceTouchConfiguration? = nil) -> ForceTouchControlEvent { |
||||
|
gesture(make(configuration: configuration)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension ObservableType where Element: ForceTouchGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Maps the observable `GestureRecognizer` events sequence to an observable sequence of force values. |
||||
|
*/ |
||||
|
public func asForce() -> Observable<CGFloat> { |
||||
|
self.map { $0.force } |
||||
|
} |
||||
|
|
||||
|
public func when(fractionCompletedExceeds threshold: CGFloat) -> Observable<Element> { |
||||
|
let source = asObservable() |
||||
|
return source |
||||
|
.when(.began) |
||||
|
.flatMapLatest { [unowned source] _ in |
||||
|
source |
||||
|
.when(.changed) |
||||
|
.filter { |
||||
|
if threshold == 0 { |
||||
|
return $0.fractionCompleted > threshold |
||||
|
} else { |
||||
|
return $0.fractionCompleted >= threshold |
||||
|
} |
||||
|
} |
||||
|
.take(1) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private func lerp<T : FloatingPoint>(_ v0: T, _ v1: T, _ t: T) -> T { |
||||
|
v0 + (v1 - v0) * t |
||||
|
} |
||||
|
|
||||
|
private func lerp<T : FloatingPoint>(mapMin: T, to min: T, mapMax: T, to max: T, value: T) -> T { |
||||
|
lerp(min, max, (value - mapMin) / (mapMax - mapMin)) |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,136 @@ |
|||||
|
#if canImport(UIKit) |
||||
|
|
||||
|
import UIKit.UIGestureRecognizerSubclass |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
public class TouchDownGestureRecognizer: UIGestureRecognizer { |
||||
|
|
||||
|
public override init(target: Any?, action: Selector?) { |
||||
|
super.init(target: target, action: action) |
||||
|
|
||||
|
trigger |
||||
|
.flatMapFirst { [unowned self] _ -> Observable<Void> in |
||||
|
let trigger = Observable.just(()) |
||||
|
guard self.state == .possible else { |
||||
|
return trigger |
||||
|
} |
||||
|
return trigger.delay( |
||||
|
.milliseconds(Int(self.minimumTouchDuration * 1000)), |
||||
|
scheduler: MainScheduler.instance |
||||
|
) |
||||
|
} |
||||
|
.subscribe(onNext: { [unowned self] _ in |
||||
|
self.touches = self._touches |
||||
|
}) |
||||
|
.disposed(by: triggerDisposeBag) |
||||
|
} |
||||
|
|
||||
|
public var minimumTouchDuration: TimeInterval = 0 |
||||
|
|
||||
|
/** |
||||
|
When set to `false`, it allows to bypass the touch ignoring mechanism in order to get absolutely all touch down events. |
||||
|
Defaults to `true`. |
||||
|
- note: See [ignore(_ touch: UITouch, for event: UIEvent)](https://developer.apple.com/documentation/uikit/uigesturerecognizer/1620010-ignore) |
||||
|
*/ |
||||
|
public var isTouchIgnoringEnabled: Bool = true |
||||
|
|
||||
|
@nonobjc public var touches: Set<UITouch> = [] { |
||||
|
didSet { |
||||
|
if touches.isEmpty { |
||||
|
if state == .possible { |
||||
|
state = .cancelled |
||||
|
} else { |
||||
|
state = .ended |
||||
|
} |
||||
|
} else { |
||||
|
if state == .possible { |
||||
|
state = .began |
||||
|
} else { |
||||
|
state = .changed |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) { |
||||
|
super.touchesBegan(touches, with: event) |
||||
|
setTouches(from: event) |
||||
|
} |
||||
|
|
||||
|
public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) { |
||||
|
super.touchesMoved(touches, with: event) |
||||
|
setTouches(from: event) |
||||
|
} |
||||
|
|
||||
|
public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) { |
||||
|
super.touchesEnded(touches, with: event) |
||||
|
setTouches(from: event) |
||||
|
} |
||||
|
|
||||
|
public override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) { |
||||
|
super.touchesCancelled(touches, with: event) |
||||
|
setTouches(from: event) |
||||
|
} |
||||
|
|
||||
|
private let triggerDisposeBag = DisposeBag() |
||||
|
private let trigger = PublishSubject<Void>() |
||||
|
private var _touches: Set<UITouch> = [] |
||||
|
private func setTouches(from event: UIEvent) { |
||||
|
_touches = (event.allTouches ?? []).filter { touch in |
||||
|
[.began, .stationary, .moved].contains(touch.phase) |
||||
|
} |
||||
|
trigger.onNext(()) |
||||
|
} |
||||
|
|
||||
|
public override func reset() { |
||||
|
super.reset() |
||||
|
touches = [] |
||||
|
} |
||||
|
|
||||
|
public override func ignore(_ touch: UITouch, for event: UIEvent) { |
||||
|
guard isTouchIgnoringEnabled else { |
||||
|
return |
||||
|
} |
||||
|
super.ignore(touch, for: event) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public typealias TouchDownConfiguration = Configuration<TouchDownGestureRecognizer> |
||||
|
public typealias TouchDownControlEvent = ControlEvent<TouchDownGestureRecognizer> |
||||
|
public typealias TouchDownObservable = Observable<TouchDownGestureRecognizer> |
||||
|
|
||||
|
extension Factory where Gesture == RxGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Returns an `AnyFactory` for `TouchDownGestureRecognizer` |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public static func touchDown(configuration: TouchDownConfiguration? = nil) -> AnyFactory { |
||||
|
make(configuration: configuration).abstracted() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `TouchDownGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public func touchDownGesture(configuration: TouchDownConfiguration? = nil) -> TouchDownControlEvent { |
||||
|
gesture(make(configuration: configuration)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension ObservableType where Element: TouchDownGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Maps the observable `GestureRecognizer` events sequence to an observable sequence of force values. |
||||
|
*/ |
||||
|
public func asTouches() -> Observable<Set<UITouch>> { |
||||
|
self.map { $0.touches } |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,86 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
#if canImport(UIKit) |
||||
|
|
||||
|
import UIKit |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
public struct TransformGestureRecognizers { |
||||
|
public let panGesture: UIPanGestureRecognizer |
||||
|
public let rotationGesture: UIRotationGestureRecognizer |
||||
|
public let pinchGesture: UIPinchGestureRecognizer |
||||
|
} |
||||
|
|
||||
|
public struct TransformVelocity { |
||||
|
let translation: CGPoint |
||||
|
let rotation: CGFloat |
||||
|
let scale: CGFloat |
||||
|
} |
||||
|
|
||||
|
public typealias TransformControlEvent = ControlEvent<TransformGestureRecognizers> |
||||
|
public typealias TransformObservable = Observable<TransformGestureRecognizers> |
||||
|
|
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
public func transformGestures() -> TransformControlEvent { |
||||
|
let source = Observable.combineLatest(panGesture(), rotationGesture(), pinchGesture()) { |
||||
|
TransformGestureRecognizers( |
||||
|
panGesture: $0, |
||||
|
rotationGesture: $1, |
||||
|
pinchGesture: $2 |
||||
|
) |
||||
|
} |
||||
|
return ControlEvent(events: source) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension ObservableType where Element == TransformGestureRecognizers { |
||||
|
|
||||
|
public func when(_ states: RxGestureRecognizerState...) -> Observable<Element> { |
||||
|
filter { gestures in |
||||
|
states.contains(gestures.panGesture.state) |
||||
|
|| states.contains(gestures.rotationGesture.state) |
||||
|
|| states.contains(gestures.pinchGesture.state) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func asTransform(in view: TargetView = .view) -> Observable<(transform: CGAffineTransform, velocity: TransformVelocity)> { |
||||
|
map { gestures in |
||||
|
let translationView = view.targetView(for: gestures.panGesture) |
||||
|
let translation = gestures.panGesture.translation(in: translationView) |
||||
|
|
||||
|
let transform = CGAffineTransform.identity |
||||
|
.rotated(by: gestures.rotationGesture.rotation) |
||||
|
.scaledBy(x: gestures.pinchGesture.scale, y: gestures.pinchGesture.scale) |
||||
|
.translatedBy(x: translation.x, y: translation.y) |
||||
|
|
||||
|
let velocity = TransformVelocity( |
||||
|
translation: gestures.panGesture.velocity(in: translationView), |
||||
|
rotation: gestures.rotationGesture.velocity, |
||||
|
scale: gestures.pinchGesture.velocity |
||||
|
) |
||||
|
|
||||
|
return (transform, velocity) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,60 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
#if canImport(UIKit) |
||||
|
|
||||
|
import UIKit |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
|
||||
|
@available(iOS 13.0, *) |
||||
|
public typealias HoverConfiguration = Configuration<UIHoverGestureRecognizer> |
||||
|
@available(iOS 13.0, *) |
||||
|
public typealias HoverControlEvent = ControlEvent<UIHoverGestureRecognizer> |
||||
|
@available(iOS 13.0, *) |
||||
|
public typealias HoverObservable = Observable<UIHoverGestureRecognizer> |
||||
|
|
||||
|
|
||||
|
@available(iOS 13.0, *) |
||||
|
extension Factory where Gesture == RxGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Returns an `AnyFactory` for `UIHoverGestureRecognizer` |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public static func hover(configuration: HoverConfiguration? = nil) -> AnyFactory { |
||||
|
make(configuration: configuration).abstracted() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@available(iOS 13.0, *) |
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `UIHoverGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public func hoverGesture(configuration: HoverConfiguration? = nil) -> HoverControlEvent { |
||||
|
gesture(make(configuration: configuration)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,53 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
#if canImport(UIKit) |
||||
|
|
||||
|
import UIKit |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
public typealias LongPressConfiguration = Configuration<UILongPressGestureRecognizer> |
||||
|
public typealias LongPressControlEvent = ControlEvent<UILongPressGestureRecognizer> |
||||
|
public typealias LongPressObservable = Observable<UILongPressGestureRecognizer> |
||||
|
|
||||
|
extension Factory where Gesture == RxGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Returns an `AnyFactory` for `UILongPressGestureRecognizer` |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public static func longPress(configuration: LongPressConfiguration? = nil) -> AnyFactory { |
||||
|
make(configuration: configuration).abstracted() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `UILongPressGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public func longPressGesture(configuration: LongPressConfiguration? = nil) -> LongPressControlEvent { |
||||
|
gesture(make(configuration: configuration)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,71 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
#if canImport(UIKit) |
||||
|
|
||||
|
import UIKit |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
public typealias PanConfiguration = Configuration<UIPanGestureRecognizer> |
||||
|
public typealias PanControlEvent = ControlEvent<UIPanGestureRecognizer> |
||||
|
public typealias PanObservable = Observable<UIPanGestureRecognizer> |
||||
|
|
||||
|
extension Factory where Gesture == RxGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Returns an `AnyFactory` for `UIPanGestureRecognizer` |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public static func pan(configuration: PanConfiguration? = nil) -> AnyFactory { |
||||
|
make(configuration: configuration).abstracted() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `UIPanGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public func panGesture(configuration: PanConfiguration? = nil) -> PanControlEvent { |
||||
|
gesture(make(configuration: configuration)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension ObservableType where Element: UIPanGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
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. |
||||
|
|
||||
|
- parameter view: A `TargetView` value on which the gesture took place. |
||||
|
*/ |
||||
|
public func asTranslation(in view: TargetView = .view) -> Observable<(translation: CGPoint, velocity: CGPoint)> { |
||||
|
self.map { gesture in |
||||
|
let view = view.targetView(for: gesture) |
||||
|
return ( |
||||
|
gesture.translation(in: view), |
||||
|
gesture.velocity(in: view) |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,65 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
#if canImport(UIKit) |
||||
|
|
||||
|
import UIKit |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
public typealias PinchConfiguration = Configuration<UIPinchGestureRecognizer> |
||||
|
public typealias PinchControlEvent = ControlEvent<UIPinchGestureRecognizer> |
||||
|
public typealias PinchObservable = Observable<UIPinchGestureRecognizer> |
||||
|
|
||||
|
extension Factory where Gesture == RxGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Returns an `AnyFactory` for `UIPinchGestureRecognizer` |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public static func pinch(configuration: PinchConfiguration? = nil) -> AnyFactory { |
||||
|
make(configuration: configuration).abstracted() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `UIPinchGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public func pinchGesture(configuration: PinchConfiguration? = nil) -> PinchControlEvent { |
||||
|
gesture(make(configuration: configuration)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension ObservableType where Element: UIPinchGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
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. |
||||
|
*/ |
||||
|
public func asScale() -> Observable<(scale: CGFloat, velocity: CGFloat)> { |
||||
|
self.map { gesture in |
||||
|
(gesture.scale, gesture.velocity) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,65 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
#if canImport(UIKit) |
||||
|
|
||||
|
import UIKit |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
public typealias RotationConfiguration = Configuration<UIRotationGestureRecognizer> |
||||
|
public typealias RotationControlEvent = ControlEvent<UIRotationGestureRecognizer> |
||||
|
public typealias RotationObservable = Observable<UIRotationGestureRecognizer> |
||||
|
|
||||
|
extension Factory where Gesture == RxGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Returns an `AnyFactory` for `UIRotationGestureRecognizer` |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public static func rotation(configuration: RotationConfiguration? = nil) -> AnyFactory { |
||||
|
make(configuration: configuration).abstracted() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `UIRotationGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public func rotationGesture(configuration: RotationConfiguration? = nil) -> RotationControlEvent { |
||||
|
gesture(make(configuration: configuration)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension ObservableType where Element: UIRotationGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Maps the observable `GestureRecognizer` events sequence to an observable sequence of rotation values of the gesture in radians alongside the gesture velocity. |
||||
|
*/ |
||||
|
public func asRotation() -> Observable<(rotation: CGFloat, velocity: CGFloat)> { |
||||
|
self.map { gesture in |
||||
|
(gesture.rotation, gesture.velocity) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,53 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
#if canImport(UIKit) |
||||
|
|
||||
|
import UIKit |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
public typealias ScreenEdgePanConfiguration = Configuration<UIScreenEdgePanGestureRecognizer> |
||||
|
public typealias ScreenEdgePanControlEvent = ControlEvent<UIScreenEdgePanGestureRecognizer> |
||||
|
public typealias ScreenEdgePanObservable = Observable<UIScreenEdgePanGestureRecognizer> |
||||
|
|
||||
|
extension Factory where Gesture == RxGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Returns an `AnyFactory` for `UIScreenEdgePanGestureRecognizer` |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public static func screenEdgePan(configuration: ScreenEdgePanConfiguration? = nil) -> AnyFactory { |
||||
|
make(configuration: configuration).abstracted() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `UIScreenEdgePanGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public func screenEdgePanGesture(configuration: ScreenEdgePanConfiguration? = nil) -> ScreenEdgePanControlEvent { |
||||
|
gesture(make(configuration: configuration)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,95 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
#if canImport(UIKit) |
||||
|
|
||||
|
import UIKit |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
public enum SwipeDirection { |
||||
|
case right, left, up, down |
||||
|
|
||||
|
fileprivate typealias SwipeGestureRecognizerDirection = UISwipeGestureRecognizer.Direction |
||||
|
|
||||
|
fileprivate var direction: SwipeGestureRecognizerDirection { |
||||
|
switch self { |
||||
|
case .right: return .right |
||||
|
case .left: return .left |
||||
|
case .up: return .up |
||||
|
case .down: return .down |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private func make(direction: SwipeDirection, configuration: Configuration<UISwipeGestureRecognizer>?) -> Factory<UISwipeGestureRecognizer> { |
||||
|
make { |
||||
|
$0.direction = direction.direction |
||||
|
configuration?($0, $1) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public typealias SwipeConfiguration = Configuration<UISwipeGestureRecognizer> |
||||
|
public typealias SwipeControlEvent = ControlEvent<UISwipeGestureRecognizer> |
||||
|
public typealias SwipeObservable = Observable<UISwipeGestureRecognizer> |
||||
|
|
||||
|
extension Factory where Gesture == RxGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Returns an `AnyFactory` for `UISwipeGestureRecognizer` |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public static func swipe(direction: SwipeDirection, configuration: SwipeConfiguration? = nil) -> AnyFactory { |
||||
|
make(direction: direction, configuration: configuration).abstracted() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `UISwipeGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
private func swipeGesture(direction: SwipeDirection,configuration: SwipeConfiguration? = nil) -> SwipeControlEvent { |
||||
|
gesture(make(direction: direction, configuration: configuration)) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `UISwipeGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public func swipeGesture(_ directions: Set<SwipeDirection>,configuration: SwipeConfiguration? = nil) -> SwipeControlEvent { |
||||
|
let source = Observable.merge(directions.map { |
||||
|
swipeGesture(direction: $0, configuration: configuration).asObservable() |
||||
|
}) |
||||
|
return ControlEvent(events: source) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `UISwipeGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public func swipeGesture(_ directions: SwipeDirection...,configuration: SwipeConfiguration? = nil) -> SwipeControlEvent { |
||||
|
swipeGesture(Set(directions), configuration: configuration) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,53 @@ |
|||||
|
// Copyright (c) RxSwiftCommunity |
||||
|
|
||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
// of this software and associated documentation files (the "Software"), to deal |
||||
|
// in the Software without restriction, including without limitation the rights |
||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
// copies of the Software, and to permit persons to whom the Software is |
||||
|
// furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
// The above copyright notice and this permission notice shall be included in |
||||
|
// all copies or substantial portions of the Software. |
||||
|
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
// THE SOFTWARE. |
||||
|
|
||||
|
#if canImport(UIKit) |
||||
|
|
||||
|
import UIKit |
||||
|
import RxSwift |
||||
|
import RxCocoa |
||||
|
|
||||
|
public typealias TapConfiguration = Configuration<UITapGestureRecognizer> |
||||
|
public typealias TapControlEvent = ControlEvent<UITapGestureRecognizer> |
||||
|
public typealias TapObservable = Observable<UITapGestureRecognizer> |
||||
|
|
||||
|
extension Factory where Gesture == RxGestureRecognizer { |
||||
|
|
||||
|
/** |
||||
|
Returns an `AnyFactory` for `UITapGestureRecognizer` |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public static func tap(configuration: TapConfiguration? = nil) -> AnyFactory { |
||||
|
make(configuration: configuration).abstracted() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extension Reactive where Base: RxGestureView { |
||||
|
|
||||
|
/** |
||||
|
Returns an observable `UITapGestureRecognizer` events sequence |
||||
|
- parameter configuration: A closure that allows to fully configure the gesture recognizer |
||||
|
*/ |
||||
|
public func tapGesture(configuration: TapConfiguration? = nil) -> TapControlEvent { |
||||
|
gesture(make(configuration: configuration)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,216 @@ |
|||||
|
# RxGesture |
||||
|
|
||||
|
[![Version](https://img.shields.io/cocoapods/v/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture) |
||||
|
[![License](https://img.shields.io/cocoapods/l/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture) |
||||
|
[![Platform](https://img.shields.io/cocoapods/p/RxGesture.svg?style=flat)](http://cocoapods.org/pods/RxGesture) |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
![](Pod/Assets/demo.gif) |
||||
|
|
||||
|
To run the example project, clone the repo, in the __Example__ folder open `RxGesture.xcworkspace`. |
||||
|
|
||||
|
You _might_ need to run `pod install` from the Example directory first. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
__RxGesture__ allows you to easily turn any view into a tappable or swipeable control like so: |
||||
|
|
||||
|
```swift |
||||
|
view.rx |
||||
|
.tapGesture() |
||||
|
.when(.recognized) |
||||
|
.subscribe(onNext: { _ in |
||||
|
//react to taps |
||||
|
}) |
||||
|
.disposed(by: stepBag) |
||||
|
``` |
||||
|
|
||||
|
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: |
||||
|
|
||||
|
```swift |
||||
|
view.rx |
||||
|
.anyGesture(.tap(), .swipe([.up, .down])) |
||||
|
.when(.recognized) |
||||
|
.subscribe(onNext: { _ in |
||||
|
//dismiss presented photo |
||||
|
}) |
||||
|
.disposed(by: stepBag) |
||||
|
``` |
||||
|
|
||||
|
`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:)`) |
||||
|
|
||||
|
|
||||
|
#### On iOS, RxGesture supports: |
||||
|
|
||||
|
```swift |
||||
|
view.rx.tapGesture() -> ControlEvent<UITapGestureRecognizer> |
||||
|
view.rx.pinchGesture() -> ControlEvent<UIPinchGestureRecognizer> |
||||
|
view.rx.swipeGesture(.left) -> ControlEvent<UISwipeGestureRecognizer> |
||||
|
view.rx.panGesture() -> ControlEvent<UIPanGestureRecognizer> |
||||
|
view.rx.longPressGesture() -> ControlEvent<UILongPressGestureRecognizer> |
||||
|
view.rx.rotationGesture() -> ControlEvent<UIRotationGestureRecognizer> |
||||
|
view.rx.screenEdgePanGesture() -> ControlEvent<UIScreenEdgePanGestureRecognizer> |
||||
|
view.rx.hoverGesture() -> ControlEvent<UIHoverGestureRecognizer> |
||||
|
|
||||
|
view.rx.anyGesture(.tap(), ...) -> ControlEvent<UIGestureRecognizer> |
||||
|
view.rx.anyGesture(.pinch(), ...) -> ControlEvent<UIGestureRecognizer> |
||||
|
view.rx.anyGesture(.swipe(.left), ...) -> ControlEvent<UIGestureRecognizer> |
||||
|
view.rx.anyGesture(.pan(), ...) -> ControlEvent<UIGestureRecognizer> |
||||
|
view.rx.anyGesture(.longPress(), ...) -> ControlEvent<UIGestureRecognizer> |
||||
|
view.rx.anyGesture(.rotation(), ...) -> ControlEvent<UIGestureRecognizer> |
||||
|
view.rx.anyGesture(.screenEdgePan(), ...) -> ControlEvent<UIGestureRecognizer> |
||||
|
view.rx.anyGesture(.hover(), ...) -> ControlEvent<UIGestureRecognizer> |
||||
|
``` |
||||
|
|
||||
|
#### On macOS, RxGesture supports: |
||||
|
|
||||
|
```swift |
||||
|
view.rx.clickGesture() -> ControlEvent<NSClickGestureRecognizer> |
||||
|
view.rx.rightClickGesture() -> ControlEvent<NSClickGestureRecognizer> |
||||
|
view.rx.panGesture() -> ControlEvent<NSPanGestureRecognizer> |
||||
|
view.rx.pressGesture() -> ControlEvent<NSPressGestureRecognizer> |
||||
|
view.rx.rotationGesture() -> ControlEvent<NSRotationGestureRecognizer> |
||||
|
view.rx.magnificationGesture() -> ControlEvent<NSMagnificationGestureRecognizer> |
||||
|
|
||||
|
view.rx.anyGesture(.click(), ...) -> ControlEvent<NSGestureRecognizer> |
||||
|
view.rx.anyGesture(.rightClick(), ...) -> ControlEvent<NSGestureRecognizer> |
||||
|
view.rx.anyGesture(.pan(), ...) -> ControlEvent<NSGestureRecognizer> |
||||
|
view.rx.anyGesture(.press(), ...) -> ControlEvent<NSGestureRecognizer> |
||||
|
view.rx.anyGesture(.rotation(), ...) -> ControlEvent<NSGestureRecognizer> |
||||
|
view.rx.anyGesture(.magnification(), ...) -> ControlEvent<NSGestureRecognizer> |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
ℹ️ 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()`. |
||||
|
|
||||
|
|
||||
|
## Filtering State |
||||
|
|
||||
|
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`). |
||||
|
|
||||
|
Here are the preferred states that can be used for each kind of gestures (__iOS__ and __macOS__): |
||||
|
|
||||
|
Kind | States |
||||
|
---|--- |
||||
|
`.tap()` `.click()` `.rightClick()` `.swipe()`| `.recognized` |
||||
|
`.longPress()` `.press()` | `.began` |
||||
|
`.pan()` `.pinch()` `.rotation()` `.magnification()` `.screenEdgePan()` | `.began` `.changed` `.ended` |
||||
|
|
||||
|
You usually filter the state using the `.when()` operator: |
||||
|
```swift |
||||
|
view.rx.tapGesture().when(.recognized) |
||||
|
view.rx.panGesture().when(.began, .changed, .ended) |
||||
|
``` |
||||
|
|
||||
|
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: |
||||
|
|
||||
|
```swift |
||||
|
view.rx |
||||
|
.anyGesture(.tap(), .swipe([.up, .down])) |
||||
|
.when(.recognized) |
||||
|
.subscribe(onNext: { gesture in |
||||
|
// Called whenever a tap, a swipe-up or a swipe-down is recognized (state == .recognized) |
||||
|
}) |
||||
|
.disposed(by: bag) |
||||
|
|
||||
|
view.rx |
||||
|
.anyGesture( |
||||
|
(.tap(), when: .recognized), |
||||
|
(.pan(), when: .ended) |
||||
|
) |
||||
|
.subscribe(onNext: { gesture in |
||||
|
// Called whenever: |
||||
|
// - a tap is recognized (state == .recognized) |
||||
|
// - or a pan is ended (state == .ended) |
||||
|
}) |
||||
|
.disposed(by: bag) |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
__The demo app includes examples for all recognizers ➡️ [iOS](Example/RxGesture/ViewController.swift), [macOS](Example/RxGesture-OSX/ViewController.swift)__. |
||||
|
|
||||
|
## Delegate customization |
||||
|
### Lightweight customization |
||||
|
Each gesture recognizer has a default `RxGestureRecognizerDelegate`. It allows you to customize every delegate method using a policy: |
||||
|
- `.always` will return `true` to the corresponding delegate method |
||||
|
- `.never` will return `false` to the corresponding delegate method |
||||
|
- `.custom` takes an associated closure that will be executed to return a value to the corresponding delegate method |
||||
|
|
||||
|
Here are the available policies with their corresponding delegate method: |
||||
|
```swift |
||||
|
beginPolicy -> gestureRecognizerShouldBegin(:_) |
||||
|
touchReceptionPolicy -> gestureRecognizer(_:shouldReceive:) |
||||
|
selfFailureRequirementPolicy -> gestureRecognizer(_:shouldBeRequiredToFailBy:) |
||||
|
otherFailureRequirementPolicy -> gestureRecognizer(_:shouldRequireFailureOf:) |
||||
|
simultaneousRecognitionPolicy -> gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) |
||||
|
eventRecognitionAttemptPolicy -> gestureRecognizer(_:shouldAttemptToRecognizeWith:) // macOS only |
||||
|
pressReceptionPolicy -> gestureRecognizer(_:shouldReceive:) // iOS only |
||||
|
``` |
||||
|
|
||||
|
This delegate can be customized in the configuration closure: |
||||
|
```swift |
||||
|
view.rx.tapGesture(configuration: { gestureRecognizer, delegate in |
||||
|
delegate.simultaneousRecognitionPolicy = .always // (default value) |
||||
|
// or |
||||
|
delegate.simultaneousRecognitionPolicy = .never |
||||
|
// or |
||||
|
delegate.simultaneousRecognitionPolicy = .custom { gestureRecognizer, otherGestureRecognizer in |
||||
|
return otherGestureRecognizer is UIPanGestureRecognizer |
||||
|
} |
||||
|
delegate.otherFailureRequirementPolicy = .custom { gestureRecognizer, otherGestureRecognizer in |
||||
|
return otherGestureRecognizer is UILongPressGestureRecognizer |
||||
|
} |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
Default values can be found in [`RxGestureRecognizerDelegate.swift`](Pod/Classes/RxGestureRecognizerDelegate.swift#L56). |
||||
|
|
||||
|
### Full customization |
||||
|
You can also replace the default delegate by your own, or remove it. |
||||
|
```swift |
||||
|
view.rx.tapGesture { [unowned self] gestureRecognizer, delegate in |
||||
|
gestureRecognizer.delegate = nil |
||||
|
// or |
||||
|
gestureRecognizer.delegate = self |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Requirements |
||||
|
|
||||
|
This library depends on both __RxSwift__ and __RxCocoa__. |
||||
|
|
||||
|
|
||||
|
## Installation |
||||
|
|
||||
|
### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html) |
||||
|
|
||||
|
Add this to `Podfile` |
||||
|
|
||||
|
```swift |
||||
|
pod "RxGesture" |
||||
|
``` |
||||
|
|
||||
|
```bash |
||||
|
$ pod install |
||||
|
``` |
||||
|
|
||||
|
### [Carthage](https://github.com/Carthage/Carthage) |
||||
|
|
||||
|
Add this to `Cartfile` |
||||
|
|
||||
|
``` |
||||
|
github "RxSwiftCommunity/RxGesture" ~> 3.0 |
||||
|
``` |
||||
|
|
||||
|
```bash |
||||
|
$ carthage update |
||||
|
``` |
||||
|
|
||||
|
## Thanks |
||||
|
|
||||
|
Everyone in the RxSwift Slack channel 💯 |
||||
|
|
||||
|
## License |
||||
|
|
||||
|
RxGesture is available under the MIT license. See the LICENSE file for more info. |
8
Pods/Target Support Files/Pods-GME Remit/Pods-GME Remit.debug.xcconfig
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
8
Pods/Target Support Files/Pods-GME Remit/Pods-GME Remit.release.xcconfig
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
8
Pods/Target Support Files/Pods-GMERemittanceTests/Pods-GMERemittanceTests.debug.xcconfig
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
8
Pods/Target Support Files/Pods-GMERemittanceTests/Pods-GMERemittanceTests.release.xcconfig
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
8
Pods/Target Support Files/Pods-GMERemittanceUITests/Pods-GMERemittanceUITests.debug.xcconfig
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
8
Pods/Target Support Files/Pods-GMERemittanceUITests/Pods-GMERemittanceUITests.release.xcconfig
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,26 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
|
<plist version="1.0"> |
||||
|
<dict> |
||||
|
<key>CFBundleDevelopmentRegion</key> |
||||
|
<string>en</string> |
||||
|
<key>CFBundleExecutable</key> |
||||
|
<string>${EXECUTABLE_NAME}</string> |
||||
|
<key>CFBundleIdentifier</key> |
||||
|
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string> |
||||
|
<key>CFBundleInfoDictionaryVersion</key> |
||||
|
<string>6.0</string> |
||||
|
<key>CFBundleName</key> |
||||
|
<string>${PRODUCT_NAME}</string> |
||||
|
<key>CFBundlePackageType</key> |
||||
|
<string>FMWK</string> |
||||
|
<key>CFBundleShortVersionString</key> |
||||
|
<string>4.0.2</string> |
||||
|
<key>CFBundleSignature</key> |
||||
|
<string>????</string> |
||||
|
<key>CFBundleVersion</key> |
||||
|
<string>${CURRENT_PROJECT_VERSION}</string> |
||||
|
<key>NSPrincipalClass</key> |
||||
|
<string></string> |
||||
|
</dict> |
||||
|
</plist> |
@ -0,0 +1,5 @@ |
|||||
|
#import <Foundation/Foundation.h> |
||||
|
@interface PodsDummy_RxGesture : NSObject |
||||
|
@end |
||||
|
@implementation PodsDummy_RxGesture |
||||
|
@end |
@ -0,0 +1,12 @@ |
|||||
|
#ifdef __OBJC__ |
||||
|
#import <UIKit/UIKit.h> |
||||
|
#else |
||||
|
#ifndef FOUNDATION_EXPORT |
||||
|
#if defined(__cplusplus) |
||||
|
#define FOUNDATION_EXPORT extern "C" |
||||
|
#else |
||||
|
#define FOUNDATION_EXPORT extern |
||||
|
#endif |
||||
|
#endif |
||||
|
#endif |
||||
|
|
@ -0,0 +1,16 @@ |
|||||
|
#ifdef __OBJC__ |
||||
|
#import <UIKit/UIKit.h> |
||||
|
#else |
||||
|
#ifndef FOUNDATION_EXPORT |
||||
|
#if defined(__cplusplus) |
||||
|
#define FOUNDATION_EXPORT extern "C" |
||||
|
#else |
||||
|
#define FOUNDATION_EXPORT extern |
||||
|
#endif |
||||
|
#endif |
||||
|
#endif |
||||
|
|
||||
|
|
||||
|
FOUNDATION_EXPORT double RxGestureVersionNumber; |
||||
|
FOUNDATION_EXPORT const unsigned char RxGestureVersionString[]; |
||||
|
|
@ -0,0 +1,15 @@ |
|||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO |
||||
|
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RxGesture |
||||
|
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" |
||||
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 |
||||
|
LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift |
||||
|
OTHER_LDFLAGS = $(inherited) -framework "RxCocoa" -framework "RxSwift" |
||||
|
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -suppress-warnings |
||||
|
PODS_BUILD_DIR = ${BUILD_DIR} |
||||
|
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) |
||||
|
PODS_ROOT = ${SRCROOT} |
||||
|
PODS_TARGET_SRCROOT = ${PODS_ROOT}/RxGesture |
||||
|
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates |
||||
|
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} |
||||
|
SKIP_INSTALL = YES |
||||
|
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES |
@ -0,0 +1,6 @@ |
|||||
|
framework module RxGesture { |
||||
|
umbrella header "RxGesture-umbrella.h" |
||||
|
|
||||
|
export * |
||||
|
module * { export * } |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO |
||||
|
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RxGesture |
||||
|
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" |
||||
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 |
||||
|
LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift |
||||
|
OTHER_LDFLAGS = $(inherited) -framework "RxCocoa" -framework "RxSwift" |
||||
|
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -suppress-warnings |
||||
|
PODS_BUILD_DIR = ${BUILD_DIR} |
||||
|
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) |
||||
|
PODS_ROOT = ${SRCROOT} |
||||
|
PODS_TARGET_SRCROOT = ${PODS_ROOT}/RxGesture |
||||
|
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates |
||||
|
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} |
||||
|
SKIP_INSTALL = YES |
||||
|
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES |
Write
Preview
Loading…
Cancel
Save
Reference in new issue