-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathSelectGestureRecognizer.swift
117 lines (98 loc) · 3.68 KB
/
SelectGestureRecognizer.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//
// SelectGestureRecognizer.swift
// IFTTT SDK
//
// Copyright © 2019 IFTTT. All rights reserved.
//
import UIKit
class SelectGestureRecognizer: UIGestureRecognizer {
typealias HighlightHandler = ((UIView?, Bool) -> Void)
var cancelsOnForceTouch: Bool = false
private(set) var isHighlighted: Bool = false {
didSet {
performHighlight?(view, isHighlighted)
}
}
/// Transition the view to its highlighted state
/// Default implementation reduces the view's alpha to 0.8
/// Set to nil to disable highlighting
var performHighlight: HighlightHandler? = { (view: UIView?, isHighlighted: Bool) -> Void in
UIView.animate(withDuration: 0.1) {
view?.alpha = isHighlighted ? 0.8 : 1
}
}
var touchesBeganDelay: CFTimeInterval = 0.1
var touchesCancelledDistance: CGFloat = 10
private var gestureOrigin: CGPoint?
private var currentEvent: UIEvent?
override var state: UIGestureRecognizer.State {
didSet {
guard performHighlight != nil else { return }
switch state {
case .began:
isHighlighted = true
case .ended:
// Touches have ended but we're not already in the touch state which means this was a tap so show the tap animation
if isHighlighted == false {
// Set the alpha immediately to selected and then animate to the unselected state.
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 0.15, delay: 0, options: [], animations: {
self.isHighlighted = true
}) { (_) in
UIViewPropertyAnimator(duration: 0.15, curve: .easeOut, animations: {
self.isHighlighted = false
}).startAnimation()
}
} else {
isHighlighted = false
}
case .cancelled:
isHighlighted = false
default:
break
}
}
}
override func reset() {
super.reset()
gestureOrigin = nil
currentEvent = nil
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
if touches.contains(where: { $0.force > 1 }) {
return
}
currentEvent = event
if let touch = touches.first, let view = view {
gestureOrigin = touch.location(in: view)
} else {
gestureOrigin = nil
}
if delaysTouchesBegan {
DispatchQueue.main.asyncAfter(deadline: .now() + touchesBeganDelay) {
if event == self.currentEvent {
self.state = .began
}
}
} else {
state = .began
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
if touches.contains(where: { $0.force > 1 && cancelsOnForceTouch }) {
state = .cancelled
}
if let origin = gestureOrigin, let touch = touches.first, let view = view {
let current = touch.location(in: view)
let distance = sqrt(pow(current.x - origin.x, 2) + pow(current.y - origin.y, 2))
if distance > touchesCancelledDistance {
state = .cancelled
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
state = .ended
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
state = .cancelled
}
}