Skip to content

Commit c29cb93

Browse files
committed
Get rid of tags and create and destroy views on demand.
1 parent c0ab81d commit c29cb93

File tree

1 file changed

+107
-142
lines changed

1 file changed

+107
-142
lines changed

CardAnimation/AnimatedCardView/AnimatedCardsView.swift

+107-142
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public protocol AnimatedCardsViewDataSource : class {
1616

1717
public class AnimatedCardsView: UIView {
1818

19-
private var cardArray : [UIView]!
19+
private var cardArray : [UIView]! = []
2020
private lazy var gestureRecognizer : UIPanGestureRecognizer = {
2121
return UIPanGestureRecognizer(target: self, action: "scrollOnView:")
2222
}()
@@ -29,6 +29,8 @@ public class AnimatedCardsView: UIView {
2929
}
3030
}
3131

32+
public var animationsSpeed = 0.2
33+
3234
public struct Constants {
3335
struct DefaultSize {
3436
static let width : CGFloat = 400.0
@@ -47,27 +49,42 @@ public class AnimatedCardsView: UIView {
4749
let gradientBackgroundLayer = CAGradientLayer()
4850
var gestureDirection:panScrollDirection = .Up
4951

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+
5069

5170
// MARK: Initializers
5271
override init(frame: CGRect) {
53-
cardArray = []
5472
super.init(frame: frame)
5573
configure()
5674
}
5775

5876
required public init?(coder aDecoder: NSCoder) {
59-
cardArray = []
6077
super.init(coder: aDecoder)
6178
backgroundColor = UIColor.yellowColor()
6279
configure()
6380
}
6481

6582
// MARK: Config
6683
private func configure() {
67-
generateCards()
6884
configureConstants()
85+
generateCards()
6986
addGestureRecognizer(gestureRecognizer)
70-
relayoutSubViews()
87+
self.relayoutSubViewsAnimated(false)
7188
}
7289

7390
private func configureConstants() {
@@ -82,56 +99,45 @@ public class AnimatedCardsView: UIView {
8299
}
83100

84101
public func flipUp() {
85-
if frontCardTag == 1{
102+
guard currentIndex > 0 else {
86103
return
87104
}
88105

89-
guard let previousFrontView = viewWithTag(frontCardTag - 1) else{
90-
return
91-
}
106+
currentIndex--
92107

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
96112

97-
previousFrontView.hidden = false
98-
if let subView = previousFrontView.viewWithTag(10){
99-
subView.hidden = false
100-
}
101113

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)
107118
})
108119
}
109120

110121
public func flipDown() {
111-
if frontCardTag > cardCount{
122+
guard currentIndex < cardCount && cardArray.count > 0 else {
112123
return
113124
}
114125

115-
guard let frontView = viewWithTag(frontCardTag) else{
116-
return
117-
}
126+
currentIndex++
127+
128+
let frontView = cardArray.removeFirst()
118129

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)
121133
}
122134

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)
135141
})
136142

137143
}
@@ -153,17 +159,30 @@ extension AnimatedCardsView {
153159
private func generateCards() {
154160
// Clear previous configuration
155161
if cardArray.count > 0 {
156-
_ = cardArray.map({ $0.removeFromSuperview() })
162+
for view in cardArray {
163+
view.removeFromSuperview()
164+
}
157165
}
158166

159-
cardArray = (0...cardCount).map { (tagId) in
167+
cardArray = (0..<maxVisibleCardCount).map { (tagId) in
160168
let view = generateNewCardViewWithTagId(tagId)
161-
self.addSubview(view)
169+
addSubview(view)
162170
applyConstraintsToView(view)
163171
return view
164172
}
165173
}
166174

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+
167186
private func generateNewCardViewWithTagId(tagId:NSInteger) -> UIView {
168187
let view = UIView()
169188
view.translatesAutoresizingMaskIntoConstraints = false
@@ -197,114 +216,60 @@ extension AnimatedCardsView {
197216

198217
// MARK: Handle Layout
199218
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) {
201221
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
240238
}
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
266246
}
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)
277255
}
278-
}
256+
self.layoutIfNeeded()
257+
}, completion: { _ in
258+
if delete {
259+
subView.removeFromSuperview()
260+
}
261+
})
279262
}
280263

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)
295269
}
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()
302272
}
303-
304-
UIView.animateWithDuration(0.1, animations: {
305-
self.layoutIfNeeded()
306-
})
307-
308273
}
309274

310275
//MARK: Helper Method

0 commit comments

Comments
 (0)