1
+ //
2
+ // ViewManager.swift
3
+ // ZLSwipeableViewSwift
4
+ //
5
+ // Created by Andrew Breckenridge on 5/17/16.
6
+ // Copyright © 2016 Andrew Breckenridge. All rights reserved.
7
+ //
8
+
9
+ import UIKit
10
+
11
+ class ViewManager : NSObject {
12
+
13
+ // Snapping -> [Moving]+ -> Snapping
14
+ // Snapping -> [Moving]+ -> Swiping -> Snapping
15
+ enum State {
16
+ case Snapping( CGPoint ) , Moving( CGPoint ) , Swiping( CGPoint , CGVector )
17
+ }
18
+
19
+ var state : State {
20
+ didSet {
21
+ if case . Snapping( _) = oldValue, case let . Moving( point) = state {
22
+ unsnapView ( )
23
+ attachView ( toPoint: point)
24
+ } else if case . Snapping( _) = oldValue, case let . Swiping( origin, direction) = state {
25
+ unsnapView ( )
26
+ attachView ( toPoint: origin)
27
+ pushView ( fromPoint: origin, inDirection: direction)
28
+ } else if case . Moving( _) = oldValue, case let . Moving( point) = state {
29
+ moveView ( toPoint: point)
30
+ } else if case . Moving( _) = oldValue, case let . Snapping( point) = state {
31
+ detachView ( )
32
+ snapView ( point)
33
+ } else if case . Moving( _) = oldValue, case let . Swiping( origin, direction) = state {
34
+ pushView ( fromPoint: origin, inDirection: direction)
35
+ } else if case . Swiping( _, _) = oldValue, case let . Snapping( point) = state {
36
+ unpushView ( )
37
+ detachView ( )
38
+ snapView ( point)
39
+ }
40
+ }
41
+ }
42
+
43
+ /// To be added to view and removed
44
+ private class ZLPanGestureRecognizer : UIPanGestureRecognizer { }
45
+ private class ZLTapGestureRecognizer : UITapGestureRecognizer { }
46
+
47
+ static private let anchorViewWidth = CGFloat ( 1000 )
48
+ private var anchorView = UIView ( frame: CGRect ( x: 0 , y: 0 , width: anchorViewWidth, height: anchorViewWidth) )
49
+
50
+ private var snapBehavior : UISnapBehavior !
51
+ private var viewToAnchorViewAttachmentBehavior : UIAttachmentBehavior !
52
+ private var anchorViewToPointAttachmentBehavior : UIAttachmentBehavior !
53
+ private var pushBehavior : UIPushBehavior !
54
+
55
+ private let view : UIView
56
+ private let containerView : UIView
57
+ private let miscContainerView : UIView
58
+ private let animator : UIDynamicAnimator
59
+ private weak var swipeableView : ZLSwipeableView ?
60
+
61
+ init ( view: UIView , containerView: UIView , index: Int , miscContainerView: UIView , animator: UIDynamicAnimator , swipeableView: ZLSwipeableView ) {
62
+ self . view = view
63
+ self . containerView = containerView
64
+ self . miscContainerView = miscContainerView
65
+ self . animator = animator
66
+ self . swipeableView = swipeableView
67
+ self . state = ViewManager . defaultSnappingState ( view)
68
+
69
+ super. init ( )
70
+
71
+ view. addGestureRecognizer ( ZLPanGestureRecognizer ( target: self , action: #selector( ViewManager . handlePan ( _: ) ) ) )
72
+ view. addGestureRecognizer ( ZLTapGestureRecognizer ( target: self , action: #selector( ViewManager . handleTap ( _: ) ) ) )
73
+ miscContainerView. addSubview ( anchorView)
74
+ containerView. insertSubview ( view, atIndex: index)
75
+ }
76
+
77
+ static func defaultSnappingState( view: UIView ) -> State {
78
+ return . Snapping( view. convertPoint ( view. center, fromView: view. superview) )
79
+ }
80
+
81
+ func snappingStateAtContainerCenter( ) -> State {
82
+ guard let swipeableView = swipeableView else { return ViewManager . defaultSnappingState ( view) }
83
+ return . Snapping( containerView. convertPoint ( swipeableView. center, fromView: swipeableView. superview) )
84
+ }
85
+
86
+ deinit {
87
+ if let snapBehavior = snapBehavior {
88
+ removeBehavior ( snapBehavior)
89
+ }
90
+ if let viewToAnchorViewAttachmentBehavior = viewToAnchorViewAttachmentBehavior {
91
+ removeBehavior ( viewToAnchorViewAttachmentBehavior)
92
+ }
93
+ if let anchorViewToPointAttachmentBehavior = anchorViewToPointAttachmentBehavior {
94
+ removeBehavior ( anchorViewToPointAttachmentBehavior)
95
+ }
96
+ if let pushBehavior = pushBehavior {
97
+ removeBehavior ( pushBehavior)
98
+ }
99
+
100
+ for gestureRecognizer in view. gestureRecognizers! {
101
+ if gestureRecognizer. isKindOfClass ( ZLPanGestureRecognizer . classForCoder ( ) ) {
102
+ view. removeGestureRecognizer ( gestureRecognizer)
103
+ }
104
+ }
105
+
106
+ anchorView. removeFromSuperview ( )
107
+ view. removeFromSuperview ( )
108
+ }
109
+
110
+ func handlePan( recognizer: UIPanGestureRecognizer ) {
111
+ guard let swipeableView = swipeableView else { return }
112
+
113
+ let translation = recognizer. translationInView ( containerView)
114
+ let location = recognizer. locationInView ( containerView)
115
+ let velocity = recognizer. velocityInView ( containerView)
116
+ let movement = Movement ( location: location, translation: translation, velocity: velocity)
117
+
118
+ switch recognizer. state {
119
+ case . Began:
120
+ guard case . Snapping( _) = state else { return }
121
+ state = . Moving( location)
122
+ swipeableView. didStart ? ( view: view, atLocation: location)
123
+ case . Changed:
124
+ guard case . Moving( _) = state else { return }
125
+ state = . Moving( location)
126
+ swipeableView. swiping ? ( view: view, atLocation: location, translation: translation)
127
+ case . Ended, . Cancelled:
128
+ guard case . Moving( _) = state else { return }
129
+ if swipeableView. shouldSwipeView ( view: view, movement: movement, swipeableView: swipeableView) {
130
+ let directionVector = CGVector ( point: translation. normalized * max( velocity. magnitude, swipeableView. minVelocityInPointPerSecond) )
131
+ state = . Swiping( location, directionVector)
132
+ swipeableView. swipeView ( view, location: location, directionVector: directionVector)
133
+ } else {
134
+ state = snappingStateAtContainerCenter ( )
135
+ swipeableView. didCancel ? ( view: view)
136
+ }
137
+ swipeableView. didEnd ? ( view: view, atLocation: location)
138
+ default :
139
+ break
140
+ }
141
+ }
142
+
143
+ func handleTap( recognizer: UITapGestureRecognizer ) {
144
+ guard let swipeableView = swipeableView, topView = swipeableView. topView ( ) else { return }
145
+
146
+ let location = recognizer. locationInView ( containerView)
147
+ swipeableView. didTap ? ( view: topView, atLocation: location)
148
+ }
149
+
150
+ private func snapView( point: CGPoint ) {
151
+ snapBehavior = UISnapBehavior ( item: view, snapToPoint: point)
152
+ snapBehavior!. damping = 0.75
153
+ addBehavior ( snapBehavior)
154
+ }
155
+
156
+ private func unsnapView( ) {
157
+ guard let snapBehavior = snapBehavior else { return }
158
+ removeBehavior ( snapBehavior)
159
+ }
160
+
161
+ private func attachView( toPoint point: CGPoint ) {
162
+ anchorView. center = point
163
+ anchorView. backgroundColor = UIColor . blueColor ( )
164
+ anchorView. hidden = true
165
+
166
+ // attach aView to anchorView
167
+ let p = view. center
168
+ viewToAnchorViewAttachmentBehavior = UIAttachmentBehavior ( item: view, offsetFromCenter: UIOffset ( horizontal: - ( p. x - point. x) , vertical: - ( p. y - point. y) ) , attachedToItem: anchorView, offsetFromCenter: UIOffsetZero)
169
+ viewToAnchorViewAttachmentBehavior!. length = 0
170
+
171
+ // attach anchorView to point
172
+ anchorViewToPointAttachmentBehavior = UIAttachmentBehavior ( item: anchorView, offsetFromCenter: UIOffsetZero, attachedToAnchor: point)
173
+ anchorViewToPointAttachmentBehavior!. damping = 100
174
+ anchorViewToPointAttachmentBehavior!. length = 0
175
+
176
+ addBehavior ( viewToAnchorViewAttachmentBehavior!)
177
+ addBehavior ( anchorViewToPointAttachmentBehavior!)
178
+ }
179
+
180
+ private func moveView( toPoint point: CGPoint ) {
181
+ guard let _ = viewToAnchorViewAttachmentBehavior, let toPoint = anchorViewToPointAttachmentBehavior else { return }
182
+ toPoint. anchorPoint = point
183
+ }
184
+
185
+ private func detachView( ) {
186
+ guard let viewToAnchorViewAttachmentBehavior = viewToAnchorViewAttachmentBehavior, let anchorViewToPointAttachmentBehavior = anchorViewToPointAttachmentBehavior else { return }
187
+ removeBehavior ( viewToAnchorViewAttachmentBehavior)
188
+ removeBehavior ( anchorViewToPointAttachmentBehavior)
189
+ }
190
+
191
+ private func pushView( fromPoint point: CGPoint , inDirection direction: CGVector ) {
192
+ guard let _ = viewToAnchorViewAttachmentBehavior, let anchorViewToPointAttachmentBehavior = anchorViewToPointAttachmentBehavior else { return }
193
+
194
+ removeBehavior ( anchorViewToPointAttachmentBehavior)
195
+
196
+ pushBehavior = UIPushBehavior ( items: [ anchorView] , mode: . Instantaneous)
197
+ pushBehavior. pushDirection = direction
198
+ addBehavior ( pushBehavior)
199
+ }
200
+
201
+ private func unpushView( ) {
202
+ guard let pushBehavior = pushBehavior else { return }
203
+ removeBehavior ( pushBehavior)
204
+ }
205
+
206
+ private func addBehavior( behavior: UIDynamicBehavior ) {
207
+ animator. addBehavior ( behavior)
208
+ }
209
+
210
+ private func removeBehavior( behavior: UIDynamicBehavior ) {
211
+ animator. removeBehavior ( behavior)
212
+ }
213
+
214
+ }
0 commit comments