@@ -16,7 +16,7 @@ public protocol AnimatedCardsViewDataSource : class {
16
16
17
17
public class AnimatedCardsView : UIView {
18
18
19
- private var cardArray : [ UIView ] !
19
+ private var cardArray : [ UIView ] ! = [ ]
20
20
private lazy var gestureRecognizer : UIPanGestureRecognizer = {
21
21
return UIPanGestureRecognizer ( target: self , action: " scrollOnView: " )
22
22
} ( )
@@ -29,6 +29,8 @@ public class AnimatedCardsView: UIView {
29
29
}
30
30
}
31
31
32
+ public var animationsSpeed = 0.2
33
+
32
34
public struct Constants {
33
35
struct DefaultSize {
34
36
static let width : CGFloat = 400.0
@@ -47,27 +49,42 @@ public class AnimatedCardsView: UIView {
47
49
let gradientBackgroundLayer = CAGradientLayer ( )
48
50
var gestureDirection : panScrollDirection = . Up
49
51
52
+ private var currentIndex = 0
53
+
54
+ private lazy var flipUpTransform3D : CATransform3D = {
55
+ var transform = CATransform3DIdentity
56
+ transform. m34 = - 1.0 / 1000.0
57
+ transform = CATransform3DRotate ( transform, 0 , 1 , 0 , 0 )
58
+ return transform
59
+ } ( )
60
+
61
+ private lazy var flipDownTransform3D : CATransform3D = {
62
+ var transform = CATransform3DIdentity
63
+ transform. m34 = - 1.0 / 1000.0
64
+ //此处有个很大的问题,折磨了我几个小时。原来官方的实现有个临界问题,旋转180度不会执行,其他的角度则没有问题
65
+ transform = CATransform3DRotate ( transform, CGFloat ( - M_PI) * 0.99 , 1 , 0 , 0 )
66
+ return transform
67
+ } ( )
68
+
50
69
51
70
// MARK: Initializers
52
71
override init ( frame: CGRect ) {
53
- cardArray = [ ]
54
72
super. init ( frame: frame)
55
73
configure ( )
56
74
}
57
75
58
76
required public init ? ( coder aDecoder: NSCoder ) {
59
- cardArray = [ ]
60
77
super. init ( coder: aDecoder)
61
78
backgroundColor = UIColor . yellowColor ( )
62
79
configure ( )
63
80
}
64
81
65
82
// MARK: Config
66
83
private func configure( ) {
67
- generateCards ( )
68
84
configureConstants ( )
85
+ generateCards ( )
69
86
addGestureRecognizer ( gestureRecognizer)
70
- relayoutSubViews ( )
87
+ self . relayoutSubViewsAnimated ( false )
71
88
}
72
89
73
90
private func configureConstants( ) {
@@ -82,56 +99,45 @@ public class AnimatedCardsView: UIView {
82
99
}
83
100
84
101
public func flipUp( ) {
85
- if frontCardTag == 1 {
102
+ guard currentIndex > 0 else {
86
103
return
87
104
}
88
105
89
- guard let previousFrontView = viewWithTag ( frontCardTag - 1 ) else {
90
- return
91
- }
106
+ currentIndex--
92
107
93
- var flipUpTransform3D = CATransform3DIdentity
94
- flipUpTransform3D. m34 = - 1.0 / 1000.0
95
- flipUpTransform3D = CATransform3DRotate ( flipUpTransform3D, 0 , 1 , 0 , 0 )
108
+ let newView = addNewCardViewWithIndex ( currentIndex)
109
+ newView. layer. transform = flipDownTransform3D
110
+
111
+ let shouldRemoveLast = cardArray. count > maxVisibleCardCount
96
112
97
- previousFrontView. hidden = false
98
- if let subView = previousFrontView. viewWithTag ( 10 ) {
99
- subView. hidden = false
100
- }
101
113
102
- UIView . animateWithDuration ( 0.2 , animations: {
103
- previousFrontView. layer. transform = flipUpTransform3D
104
- } , completion: {
105
- _ in
106
- self . adjustUpViewLayout ( )
114
+ UIView . animateWithDuration ( animationsSpeed, animations: {
115
+ newView. layer. transform = self . flipUpTransform3D
116
+ } , completion: { _ in
117
+ self . relayoutSubViewsAnimated ( true , removeLast: shouldRemoveLast)
107
118
} )
108
119
}
109
120
110
121
public func flipDown( ) {
111
- if frontCardTag > cardCount{
122
+ guard currentIndex < cardCount && cardArray . count > 0 else {
112
123
return
113
124
}
114
125
115
- guard let frontView = viewWithTag ( frontCardTag ) else {
116
- return
117
- }
126
+ currentIndex ++
127
+
128
+ let frontView = cardArray . removeFirst ( )
118
129
119
- if let subView = frontView. viewWithTag ( 10 ) {
120
- subView. hidden = true
130
+ var newView : UIView ? = nil
131
+ if currentIndex + cardArray. count < cardCount {
132
+ newView = addNewCardViewWithIndex ( currentIndex, insertOnRear: true )
121
133
}
122
134
123
- var flipDownTransform3D = CATransform3DIdentity
124
- flipDownTransform3D. m34 = - 1.0 / 1000.0
125
- //此处有个很大的问题,折磨了我几个小时。原来官方的实现有个临界问题,旋转180度不会执行,其他的角度则没有问题
126
- flipDownTransform3D = CATransform3DRotate ( flipDownTransform3D, CGFloat ( - M_PI) * 0.99 , 1 , 0 , 0 )
127
- UIView . animateWithDuration ( 0.3 , animations: {
128
- frontView. layer. transform = flipDownTransform3D
129
- } , completion: {
130
- _ in
131
-
132
- frontView. hidden = true
133
- self . adjustDownViewLayout ( )
134
-
135
+ UIView . animateWithDuration ( animationsSpeed*1. 5 , animations: {
136
+ newView? . layer. transform = self . flipUpTransform3D
137
+ frontView. layer. transform = self . flipDownTransform3D
138
+ } , completion: { _ in
139
+ frontView. removeFromSuperview ( )
140
+ self . relayoutSubViewsAnimated ( true )
135
141
} )
136
142
137
143
}
@@ -153,17 +159,30 @@ extension AnimatedCardsView {
153
159
private func generateCards( ) {
154
160
// Clear previous configuration
155
161
if cardArray. count > 0 {
156
- _ = cardArray. map ( { $0. removeFromSuperview ( ) } )
162
+ for view in cardArray {
163
+ view. removeFromSuperview ( )
164
+ }
157
165
}
158
166
159
- cardArray = ( 0 ... cardCount ) . map { ( tagId) in
167
+ cardArray = ( 0 ..< maxVisibleCardCount ) . map { ( tagId) in
160
168
let view = generateNewCardViewWithTagId ( tagId)
161
- self . addSubview ( view)
169
+ addSubview ( view)
162
170
applyConstraintsToView ( view)
163
171
return view
164
172
}
165
173
}
166
174
175
+ private func addNewCardViewWithIndex( index: Int , insertOnRear rear: Bool = false ) -> UIView {
176
+ let newIndex = rear ? subviews. count : 0
177
+ let newView = generateNewCardViewWithTagId ( index)
178
+ rear ? insertSubview ( newView, atIndex: newIndex) : addSubview ( newView)
179
+ rear ? cardArray. append ( newView) : cardArray. insert ( newView, atIndex: newIndex)
180
+ applyConstraintsToView ( newView)
181
+ relayoutSubView ( newView, relativeIndex: newIndex, animated: false )
182
+ newView. alpha = rear ? 0.0 : 1.0
183
+ return newView
184
+ }
185
+
167
186
private func generateNewCardViewWithTagId( tagId: NSInteger ) -> UIView {
168
187
let view = UIView ( )
169
188
view. translatesAutoresizingMaskIntoConstraints = false
@@ -197,114 +216,60 @@ extension AnimatedCardsView {
197
216
198
217
// MARK: Handle Layout
199
218
extension AnimatedCardsView {
200
- func relayoutSubViewWith( viewTag: Int , relativeIndex: Int , delay: NSTimeInterval , haveBorderWidth: Bool ) {
219
+
220
+ func relayoutSubView( subView: UIView , relativeIndex: Int , animated: Bool = true , delay: NSTimeInterval = 0 , haveBorderWidth: Bool = true , fadeAndDelete delete: Bool = false ) {
201
221
let width = Constants . DefaultSize. width
202
- if let subView = self . viewWithTag ( viewTag) {
203
-
204
- subView. layer. anchorPoint = CGPointMake ( 0.5 , 1 )
205
-
206
- // if let nestedImageView = subView.viewWithTag(10) as? UIImageView{
207
- // nestedImageView.image = cardImageAtIndex(viewTag - 1)
208
- // }
209
-
210
- subView. layer. zPosition = CGFloat ( 1000 - relativeIndex)
211
- subView. alpha = calculateAlphaForIndex ( relativeIndex)
212
-
213
- var borderWidth : CGFloat = 0
214
- let filterSubViewConstraints = subView. constraints. filter ( { $0. firstAttribute == . Width && $0. secondItem == nil } )
215
- if filterSubViewConstraints. count > 0 {
216
- let widthConstraint = filterSubViewConstraints [ 0 ]
217
- let widthScale = calculateWidthScaleForIndex ( relativeIndex)
218
- widthConstraint. constant = widthScale * width
219
- borderWidth = width * widthScale / 100
220
- }
221
-
222
- let filteredViewConstraints = self . constraints. filter ( { $0. firstItem as? UIView == subView && $0. secondItem as? UIView == self && $0. firstAttribute == . CenterY} )
223
- if filteredViewConstraints. count > 0 {
224
- let centerYConstraint = filteredViewConstraints [ 0 ]
225
- let subViewHeight = calculateWidthScaleForIndex ( relativeIndex) * width * ( 1 / Constants. DefaultSize. ratio)
226
- let YOffset = calculusYOffsetForIndex ( relativeIndex)
227
- centerYConstraint. constant = subViewHeight/ 2 - YOffset
228
- }
229
-
230
- if haveBorderWidth{
231
- subView. layer. borderWidth = borderWidth
232
- } else {
233
- subView. layer. borderWidth = 0
234
- }
235
-
236
-
237
- UIView . animateWithDuration ( 0.2 , delay: delay, options: UIViewAnimationOptions . BeginFromCurrentState, animations: {
238
- self . layoutIfNeeded ( )
239
- } , completion: nil )
222
+ subView. layer. anchorPoint = CGPointMake ( 0.5 , 1 )
223
+
224
+ // if let nestedImageView = subView.viewWithTag(10) as? UIImageView{
225
+ // nestedImageView.image = cardImageAtIndex(viewTag - 1)
226
+ // }
227
+
228
+ subView. layer. zPosition = CGFloat ( 1000 - relativeIndex)
229
+
230
+
231
+ var borderWidth : CGFloat = 0
232
+ let filterSubViewConstraints = subView. constraints. filter ( { $0. firstAttribute == . Width && $0. secondItem == nil } )
233
+ if filterSubViewConstraints. count > 0 {
234
+ let widthConstraint = filterSubViewConstraints [ 0 ]
235
+ let widthScale = calculateWidthScaleForIndex ( relativeIndex)
236
+ widthConstraint. constant = widthScale * width
237
+ borderWidth = width * widthScale / 100
240
238
}
241
- }
242
-
243
- func adjustUpViewLayout( ) {
244
- if frontCardTag >= 2 {
245
- let endCardTag = cardCount - frontCardTag > maxVisibleCardCount - 1 ? ( frontCardTag + maxVisibleCardCount - 1 ) : cardCount
246
- let feed : UInt32 = 2
247
- let randomRoll = arc4random_uniform ( feed)
248
- switch randomRoll{
249
- case 0 :
250
- for var viewTag = frontCardTag; viewTag <= endCardTag; ++ viewTag{
251
- let delay : NSTimeInterval = Double ( viewTag - frontCardTag) * 0.1
252
- let relativeIndex = viewTag - frontCardTag + 1
253
- relayoutSubViewWith ( viewTag, relativeIndex: relativeIndex, delay: delay, haveBorderWidth: true )
254
- }
255
- case 1 :
256
- for var viewTag = endCardTag; viewTag >= frontCardTag; -- viewTag{
257
- let delay : NSTimeInterval = Double ( cardCount - viewTag) * 0.1
258
- let relativeIndex = viewTag - frontCardTag + 1
259
- relayoutSubViewWith ( viewTag, relativeIndex: relativeIndex, delay: delay, haveBorderWidth: true )
260
- }
261
- default :
262
- print ( " NOT YET " )
263
- }
264
-
265
- frontCardTag -= 1
239
+
240
+ let filteredViewConstraints = self . constraints. filter ( { $0. firstItem as? UIView == subView && $0. secondItem as? UIView == self && $0. firstAttribute == . CenterY} )
241
+ if filteredViewConstraints. count > 0 {
242
+ let centerYConstraint = filteredViewConstraints [ 0 ]
243
+ let subViewHeight = calculateWidthScaleForIndex ( relativeIndex) * width * ( 1 / Constants. DefaultSize. ratio)
244
+ let YOffset = calculusYOffsetForIndex ( relativeIndex)
245
+ centerYConstraint. constant = subViewHeight/ 2 - YOffset
266
246
}
267
- }
268
-
269
- func adjustDownViewLayout( ) {
270
- frontCardTag += 1
271
- let endCardTag = cardCount - frontCardTag > maxVisibleCardCount - 1 ? ( frontCardTag + maxVisibleCardCount - 1 ) : cardCount
272
- if frontCardTag <= endCardTag{
273
- for viewTag in frontCardTag... endCardTag{
274
- let delay : NSTimeInterval = 0.1 * Double( viewTag - frontCardTag)
275
- let relativeIndex = viewTag - frontCardTag
276
- relayoutSubViewWith ( viewTag, relativeIndex: relativeIndex, delay: delay, haveBorderWidth: true )
247
+
248
+ subView. layer. borderWidth = haveBorderWidth ? borderWidth : 0
249
+
250
+ UIView . animateWithDuration ( animated ? animationsSpeed : 0 , delay: delay, options: UIViewAnimationOptions . BeginFromCurrentState, animations: {
251
+ if delete {
252
+ subView. alpha = 0
253
+ } else {
254
+ subView. alpha = self . calculateAlphaForIndex ( relativeIndex)
277
255
}
278
- }
256
+ self . layoutIfNeeded ( )
257
+ } , completion: { _ in
258
+ if delete {
259
+ subView. removeFromSuperview ( )
260
+ }
261
+ } )
279
262
}
280
263
281
- func relayoutSubViews( ) {
282
- let endCardTag = cardCount - frontCardTag > maxVisibleCardCount - 1 ? ( frontCardTag + maxVisibleCardCount - 1 ) : cardCount
283
- if frontCardTag <= endCardTag{
284
- for viewTag in frontCardTag... endCardTag{
285
- if let subView = self . viewWithTag ( viewTag) {
286
-
287
- let relativeIndex = viewTag - frontCardTag
288
- let delay : NSTimeInterval = 0
289
-
290
- subView. layer. borderColor = UIColor . whiteColor ( ) . CGColor
291
- relayoutSubViewWith ( viewTag, relativeIndex: relativeIndex, delay: delay, haveBorderWidth: true )
292
-
293
- }
294
- }
264
+ func relayoutSubViewsAnimated( animated: Bool , removeLast remove: Bool = false ) {
265
+ for (index, view) in cardArray. enumerate ( ) {
266
+ let shouldDelete = remove && index == cardArray. count- 1
267
+ let delay = animated ? 0.1 * Double( index) : 0
268
+ relayoutSubView ( view, relativeIndex: index, delay: delay, fadeAndDelete: shouldDelete)
295
269
}
296
-
297
- //adjust hiddened views
298
- if frontCardTag > 1 {
299
- for viewTag in 1 ..< frontCardTag{
300
- relayoutSubViewWith ( viewTag, relativeIndex: 0 , delay: 0 , haveBorderWidth: false )
301
- }
270
+ if remove {
271
+ cardArray. removeLast ( )
302
272
}
303
-
304
- UIView . animateWithDuration ( 0.1 , animations: {
305
- self . layoutIfNeeded ( )
306
- } )
307
-
308
273
}
309
274
310
275
//MARK: Helper Method
0 commit comments