Skip to content

Commit 5b68844

Browse files
committedOct 14, 2015
Port all the View Controller code to the Component (no real improvements in code, just refactor and adaptations)
1 parent f6744c6 commit 5b68844

File tree

2 files changed

+303
-9
lines changed

2 files changed

+303
-9
lines changed
 

‎CardAnimation/AnimatedCardView/AnimatedCardsView.swift

+301-9
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,49 @@
88

99
import UIKit
1010

11+
public protocol AnimatedCardsViewDataSource : class {
1112

12-
protocol AnimatedCardsViewDataSource : class {
13-
1413
}
1514

1615
public class AnimatedCardsView: UIView {
1716

1817
private var cardArray : [UIView]!
1918

19+
public weak var dataSource : AnimatedCardsViewDataSource?
20+
2021
public struct Constants {
2122
struct DefaultSize {
22-
static let width = 400.0
23-
static let height = 300.0
23+
static let width : CGFloat = 400.0
24+
static let ratio : CGFloat = 3.0 / 4.0
2425
}
2526
static let numberOfCards = 8
2627
}
2728

29+
var frontCardTag = 1
30+
var cardCount = 0
31+
let maxVisibleCardCount = 8
32+
let gradientBackgroundLayer = CAGradientLayer()
33+
var gestureDirection:panScrollDirection = .Up
34+
2835
override init(frame: CGRect) {
2936
cardArray = []
3037
super.init(frame: frame)
31-
generateCards()
38+
configure()
3239
}
3340

3441
required public init?(coder aDecoder: NSCoder) {
3542
cardArray = []
3643
super.init(coder: aDecoder)
3744
backgroundColor = UIColor.yellowColor()
45+
configure()
46+
}
47+
48+
private func configure() {
3849
generateCards()
50+
let scrollGesture = UIPanGestureRecognizer(target: self, action: "scrollOnView:")
51+
self.addGestureRecognizer(scrollGesture)
52+
cardCount = Constants.numberOfCards
53+
relayoutSubViews()
3954
}
4055

4156

@@ -53,20 +68,297 @@ public class AnimatedCardsView: UIView {
5368
private func generateNewCardViewWithTagId(tagId:NSInteger) -> UIView {
5469
let view = UIView()
5570
view.translatesAutoresizingMaskIntoConstraints = false
56-
view.tag = tagId
57-
view.backgroundColor = UIColor.purpleColor()
71+
view.tag = tagId+1
72+
switch tagId {
73+
case 0: view.backgroundColor = UIColor.purpleColor()
74+
case 1: view.backgroundColor = UIColor.redColor()
75+
case 2: view.backgroundColor = UIColor.blackColor()
76+
case 3: view.backgroundColor = UIColor.greenColor()
77+
case 4: view.backgroundColor = UIColor.brownColor()
78+
case 5: view.backgroundColor = UIColor.darkGrayColor()
79+
case 6: view.backgroundColor = UIColor.blueColor()
80+
case 7: view.backgroundColor = UIColor.orangeColor()
81+
default: view.backgroundColor = UIColor.whiteColor()
82+
}
5883
return view
5984
}
6085

6186
private func applyConstraintsToView(view:UIView) {
6287
view.addConstraints([
63-
NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: CGFloat(1.0), constant: CGFloat(Constants.DefaultSize.width)),
64-
NSLayoutConstraint(item: view, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: CGFloat(1.0), constant: CGFloat(Constants.DefaultSize.height)),
88+
NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: CGFloat(1.0), constant: Constants.DefaultSize.width),
89+
NSLayoutConstraint(item: view, attribute: .Height, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: Constants.DefaultSize.ratio, constant: 0),
6590
])
6691
view.superview!.addConstraints([
6792
NSLayoutConstraint(item: view, attribute: .CenterX, relatedBy: .Equal, toItem: view.superview, attribute: .CenterX, multiplier: CGFloat(1.0), constant: 0),
6893
NSLayoutConstraint(item: view, attribute: .CenterY, relatedBy: .Equal, toItem: view.superview, attribute: .CenterY, multiplier: CGFloat(1.0), constant: 0),
6994
])
7095
}
96+
97+
public func flipUp() {
98+
if frontCardTag == 1{
99+
return
100+
}
101+
102+
guard let previousFrontView = viewWithTag(frontCardTag - 1) else{
103+
return
104+
}
105+
106+
var flipUpTransform3D = CATransform3DIdentity
107+
flipUpTransform3D.m34 = -1.0 / 1000.0
108+
flipUpTransform3D = CATransform3DRotate(flipUpTransform3D, 0, 1, 0, 0)
109+
110+
previousFrontView.hidden = false
111+
if let subView = previousFrontView.viewWithTag(10){
112+
subView.hidden = false
113+
}
114+
115+
UIView.animateWithDuration(0.2, animations: {
116+
previousFrontView.layer.transform = flipUpTransform3D
117+
}, completion: {
118+
_ in
119+
self.adjustUpViewLayout()
120+
})
121+
}
122+
123+
public func flipDown() {
124+
if frontCardTag > cardCount{
125+
return
126+
}
127+
128+
guard let frontView = viewWithTag(frontCardTag) else{
129+
return
130+
}
131+
132+
if let subView = frontView.viewWithTag(10){
133+
subView.hidden = true
134+
}
135+
136+
var flipDownTransform3D = CATransform3DIdentity
137+
flipDownTransform3D.m34 = -1.0 / 1000.0
138+
//此处有个很大的问题,折磨了我几个小时。原来官方的实现有个临界问题,旋转180度不会执行,其他的角度则没有问题
139+
flipDownTransform3D = CATransform3DRotate(flipDownTransform3D, CGFloat(-M_PI)*0.99, 1, 0, 0)
140+
UIView.animateWithDuration(0.3, animations: {
141+
frontView.layer.transform = flipDownTransform3D
142+
}, completion: {
143+
_ in
144+
145+
frontView.hidden = true
146+
self.adjustDownViewLayout()
147+
148+
})
149+
150+
}
151+
152+
// //MARK: Handle Screen Rotation
153+
// override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
154+
// super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
155+
// coordinator.animateAlongsideTransition({
156+
// _ in
157+
// self.gradientBackgroundLayer.frame = self.view.bounds
158+
// self.relayoutSubViews()
159+
// }, completion: nil)
160+
// }
71161

72162
}
163+
164+
165+
// MARK: Handle Layout
166+
extension AnimatedCardsView {
167+
func relayoutSubViewWith(viewTag: Int, relativeIndex:Int, delay: NSTimeInterval, haveBorderWidth: Bool){
168+
let width = Constants.DefaultSize.width
169+
if let subView = self.viewWithTag(viewTag){
170+
171+
subView.layer.anchorPoint = CGPointMake(0.5, 1)
172+
173+
// if let nestedImageView = subView.viewWithTag(10) as? UIImageView{
174+
// nestedImageView.image = cardImageAtIndex(viewTag - 1)
175+
// }
176+
177+
subView.layer.zPosition = CGFloat(1000 - relativeIndex)
178+
subView.alpha = calculateAlphaForIndex(relativeIndex)
179+
180+
var borderWidth: CGFloat = 0
181+
let filterSubViewConstraints = subView.constraints.filter({$0.firstAttribute == .Width && $0.secondItem == nil})
182+
if filterSubViewConstraints.count > 0{
183+
let widthConstraint = filterSubViewConstraints[0]
184+
let widthScale = calculateWidthScaleForIndex(relativeIndex)
185+
widthConstraint.constant = widthScale * width
186+
borderWidth = width * widthScale / 100
187+
}
188+
189+
let filteredViewConstraints = self.constraints.filter({$0.firstItem as? UIView == subView && $0.secondItem as? UIView == self && $0.firstAttribute == .CenterY})
190+
if filteredViewConstraints.count > 0{
191+
let centerYConstraint = filteredViewConstraints[0]
192+
let subViewHeight = calculateWidthScaleForIndex(relativeIndex) * width * (1/Constants.DefaultSize.ratio)
193+
let YOffset = calculusYOffsetForIndex(relativeIndex)
194+
centerYConstraint.constant = subViewHeight/2 - YOffset
195+
}
196+
197+
if haveBorderWidth{
198+
subView.layer.borderWidth = borderWidth
199+
}else{
200+
subView.layer.borderWidth = 0
201+
}
202+
203+
204+
UIView.animateWithDuration(0.2, delay: delay, options: UIViewAnimationOptions.BeginFromCurrentState, animations: {
205+
self.layoutIfNeeded()
206+
}, completion: nil)
207+
}
208+
}
209+
210+
func adjustUpViewLayout(){
211+
if frontCardTag >= 2{
212+
let endCardTag = cardCount - frontCardTag > maxVisibleCardCount - 1 ? (frontCardTag + maxVisibleCardCount - 1) : cardCount
213+
let feed: UInt32 = 2
214+
let randomRoll = arc4random_uniform(feed)
215+
switch randomRoll{
216+
case 0:
217+
for var viewTag = frontCardTag; viewTag <= endCardTag; ++viewTag{
218+
let delay: NSTimeInterval = Double(viewTag - frontCardTag)*0.1
219+
let relativeIndex = viewTag - frontCardTag + 1
220+
relayoutSubViewWith(viewTag, relativeIndex: relativeIndex, delay: delay, haveBorderWidth: true)
221+
}
222+
case 1:
223+
for var viewTag = endCardTag; viewTag >= frontCardTag; --viewTag{
224+
let delay: NSTimeInterval = Double(cardCount - viewTag) * 0.1
225+
let relativeIndex = viewTag - frontCardTag + 1
226+
relayoutSubViewWith(viewTag, relativeIndex: relativeIndex, delay: delay, haveBorderWidth: true)
227+
}
228+
default:
229+
print("NOT YET")
230+
}
231+
232+
frontCardTag -= 1
233+
}
234+
}
235+
236+
func adjustDownViewLayout(){
237+
frontCardTag += 1
238+
let endCardTag = cardCount - frontCardTag > maxVisibleCardCount - 1 ? (frontCardTag + maxVisibleCardCount - 1) : cardCount
239+
if frontCardTag <= endCardTag{
240+
for viewTag in frontCardTag...endCardTag{
241+
let delay: NSTimeInterval = 0.1 * Double(viewTag - frontCardTag)
242+
let relativeIndex = viewTag - frontCardTag
243+
relayoutSubViewWith(viewTag, relativeIndex: relativeIndex, delay: delay, haveBorderWidth: true)
244+
}
245+
}
246+
}
247+
248+
func relayoutSubViews(){
249+
let endCardTag = cardCount - frontCardTag > maxVisibleCardCount - 1 ? (frontCardTag + maxVisibleCardCount - 1) : cardCount
250+
if frontCardTag <= endCardTag{
251+
for viewTag in frontCardTag...endCardTag{
252+
if let subView = self.viewWithTag(viewTag){
253+
254+
let relativeIndex = viewTag - frontCardTag
255+
let delay: NSTimeInterval = 0
256+
257+
subView.layer.borderColor = UIColor.whiteColor().CGColor
258+
relayoutSubViewWith(viewTag, relativeIndex: relativeIndex, delay: delay, haveBorderWidth: true)
259+
260+
}
261+
}
262+
}
263+
264+
//adjust hiddened views
265+
if frontCardTag > 1{
266+
for viewTag in 1..<frontCardTag{
267+
relayoutSubViewWith(viewTag, relativeIndex: 0, delay: 0, haveBorderWidth: false)
268+
}
269+
}
270+
271+
UIView.animateWithDuration(0.1, animations: {
272+
self.layoutIfNeeded()
273+
})
274+
275+
}
276+
277+
//MARK: Helper Method
278+
//f(x) = k * x + m
279+
func calculateFactorOfFunction(x1: CGFloat, x2: CGFloat, y1: CGFloat, y2: CGFloat) -> (CGFloat, CGFloat){
280+
281+
let k = (y1-y2)/(x1-x2)
282+
let m = (x1*y2 - x2*y1)/(x1-x2)
283+
284+
return (k, m)
285+
}
286+
287+
func calculateResult(argument x: Int, k: CGFloat, m: CGFloat) -> CGFloat{
288+
return k * CGFloat(x) + m
289+
}
290+
291+
func calcuteResultWith(x1: CGFloat, x2: CGFloat, y1: CGFloat, y2: CGFloat, argument: Int) -> CGFloat{
292+
let (k, m) = calculateFactorOfFunction(x1, x2: x2, y1: y1, y2: y2)
293+
return calculateResult(argument: argument, k: k, m: m)
294+
}
295+
296+
//I set the gap between 0Card and 1st Card is 35, gap between the last two card is 15. These value on iPhone is a little big, you could make it less.
297+
//设定头两个卡片的距离为35,最后两张卡片之间的举例为15。不设定成等距才符合视觉效果。
298+
func calculusYOffsetForIndex(indexInQueue: Int) -> CGFloat{
299+
if indexInQueue < 1{
300+
return CGFloat(0)
301+
}
302+
303+
var sum: CGFloat = 0.0
304+
for i in 1...indexInQueue{
305+
var result = calcuteResultWith(1, x2: 8, y1: 35, y2: 15, argument: i)
306+
if result < 5{
307+
result = 5.0
308+
}
309+
sum += result
310+
}
311+
312+
return sum
313+
}
314+
315+
func calculateWidthScaleForIndex(indexInQueue: Int) -> CGFloat{
316+
let widthBaseScale:CGFloat = 0.5
317+
318+
var factor: CGFloat = 1
319+
if indexInQueue == 0{
320+
factor = 1
321+
}else{
322+
factor = calculateScaleFactorForIndex(indexInQueue)
323+
}
324+
325+
return widthBaseScale * factor
326+
}
327+
328+
//Zoom out card one by one.
329+
//为符合视觉以及营造景深效果,卡片依次缩小
330+
func calculateScaleFactorForIndex(indexInQueue: Int) -> CGFloat{
331+
if indexInQueue < 1{
332+
return CGFloat(1)
333+
}
334+
335+
var scale = calcuteResultWith(1, x2: 8, y1: 0.95, y2: 0.5, argument: indexInQueue)
336+
if scale < 0.1{
337+
scale = 0.1
338+
}
339+
340+
return scale
341+
}
342+
343+
func calculateAlphaForIndex(indexInQueue: Int) -> CGFloat{
344+
if indexInQueue < 1{
345+
return CGFloat(1)
346+
}
347+
348+
var alpha = calcuteResultWith(6, x2: 9, y1: 1, y2: 0.4, argument: indexInQueue)
349+
if alpha < 0.1{
350+
alpha = 0.1
351+
}else if alpha > 1{
352+
alpha = 1
353+
}
354+
355+
return alpha
356+
}
357+
358+
func calculateBorderWidthForIndex(indexInQueue: Int, initialBorderWidth: CGFloat) -> CGFloat{
359+
let scaleFactor = calculateScaleFactorForIndex(indexInQueue)
360+
return scaleFactor * initialBorderWidth
361+
}
362+
363+
}
364+

‎CardAnimation/ComponentViewController.swift

+2
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ class ComponentViewController: UIViewController {
3434
}
3535
*/
3636
@IBAction func onUpPushed(sender: UIButton) {
37+
cardsView.flipUp()
3738
}
3839

3940
@IBAction func onDownPushed(sender: UIButton) {
41+
cardsView.flipDown()
4042
}
4143

4244
}

0 commit comments

Comments
 (0)