Skip to content

Commit ce082b9

Browse files
committed
Add border width on card and update readme.
1 parent ffda857 commit ce082b9

File tree

4 files changed

+119
-81
lines changed

4 files changed

+119
-81
lines changed

Classes/seedante/UICardContainerView.swift

+57-12
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import UIKit
1010

11-
protocol CardContainerDataSource{
11+
public protocol CardContainerDataSource{
1212
func numberOfCardsForCardContainerView(cardContainerView: UICardContainerView) -> Int
1313
func cardContainerView(cardContainerView: UICardContainerView, imageForCardAtIndex: Int) -> UIImage?
1414
}
@@ -82,14 +82,16 @@ private class UICardView: UIView {
8282
}
8383

8484

85-
class UICardContainerView: UIView {
85+
public class UICardContainerView: UIView {
8686

8787
//MARK: Property to Configure
8888
//All properties must be configured before 'dataSource' is asigned, and don't change after asigned.
8989
var needsCardCenterVertically: Bool = true //The property decide card is center vertically in container, or distance of bottom between card and contaienr is the height of card.
9090
var enableBrightnessControl: Bool = true
9191
var maxVisibleCardCount: Int = 10
9292
var defaultCardSize = CGSize(width: 400, height: 300)
93+
var needsBorder: Bool = true
94+
var headCardBorderWidth: CGFloat = 5
9395

9496
var dataSource: CardContainerDataSource?{
9597
didSet{
@@ -102,14 +104,16 @@ class UICardContainerView: UIView {
102104
//MARK: Init Method
103105
override init(frame: CGRect) {
104106
super.init(frame: frame)
105-
clipsToBounds = false
107+
clipsToBounds = true
108+
translatesAutoresizingMaskIntoConstraints = false
106109
panGesture.addTarget(self, action: "panGestureAction:")
107110
addGestureRecognizer(panGesture)
108111
}
109112

110-
required init?(coder aDecoder: NSCoder) {
113+
required public init?(coder aDecoder: NSCoder) {
111114
super.init(coder: aDecoder)
112115
clipsToBounds = true
116+
translatesAutoresizingMaskIntoConstraints = false
113117
panGesture.addTarget(self, action: "panGestureAction:")
114118
addGestureRecognizer(panGesture)
115119
}
@@ -136,15 +140,21 @@ class UICardContainerView: UIView {
136140
flipDownTransform3D.m34 = -1.0 / 2000.0
137141
flipDownTransform3D = CATransform3DRotate(flipDownTransform3D, CGFloat(-M_PI), 1, 0, 0)
138142

143+
let duration: NSTimeInterval = 0.5
144+
//The animation of borderWidth change in keyFrame animation can't work, so place it in dispatch_after
145+
//本来 layer 的 borderWidth 是个可以动画的属性,但是在 UIView Animation 却不工作,没办法,只能用这种方式了
146+
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(duration * Double(NSEC_PER_SEC) / 2.0))
147+
dispatch_after(delayTime, dispatch_get_main_queue(), {
148+
headCard.layer.borderWidth = 0
149+
})
150+
139151
UIView.animateKeyframesWithDuration(0.5, delay: 0, options: UIViewKeyframeAnimationOptions(), animations: {
140152
UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 1, animations: {
141153
headCard.layer.transform = flipDownTransform3D
142154
})
143-
144155
UIView.addKeyframeWithRelativeStartTime(0.5, relativeDuration: 0.01, animations: {
145156
headCard.hiddenContent()
146157
})
147-
148158
UIView.addKeyframeWithRelativeStartTime(0.9, relativeDuration: 0.1, animations: {
149159
headCard.alpha = 0
150160
})
@@ -173,7 +183,15 @@ class UICardContainerView: UIView {
173183
let image = self.dataSource!.cardContainerView(self, imageForCardAtIndex: self.currentHeadCardIndex - 1)
174184
previousHeadCard?.setImage(image)
175185

176-
UIView.animateKeyframesWithDuration(0.5, delay: 0, options: UIViewKeyframeAnimationOptions(), animations: {
186+
//The animation of borderWidth change in keyFrame animation can't work, so place it in dispatch_after
187+
let duration: NSTimeInterval = 0.5
188+
previousHeadCard?.layer.borderWidth = 0
189+
let delayTime1 = dispatch_time(DISPATCH_TIME_NOW, Int64(duration * Double(NSEC_PER_SEC) / 2.0))
190+
dispatch_after(delayTime1, dispatch_get_main_queue(), {
191+
self.previousHeadCard?.layer.borderWidth = self.headCardBorderWidth
192+
})
193+
194+
UIView.animateKeyframesWithDuration(duration, delay: 0, options: UIViewKeyframeAnimationOptions(), animations: {
177195
UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 0.5, animations: {
178196
self.previousHeadCard?.alpha = 1
179197
})
@@ -184,10 +202,15 @@ class UICardContainerView: UIView {
184202
self.previousHeadCard?.restoreContent()
185203
})
186204
}, completion: nil)
187-
//There are some problems if the follow code in completion blovk.
188-
self.currentHeadCardIndex -= 1
189-
self.visibleCardQueue.insert(previousHeadCard!, atIndex: 0)
190-
self.layoutVisibleCardViews()
205+
206+
//There are some problems if the follow code in completion blovk, so...
207+
let delayTime2 = dispatch_time(DISPATCH_TIME_NOW, Int64(duration * Double(NSEC_PER_SEC)))
208+
dispatch_after(delayTime2, dispatch_get_main_queue(), {
209+
self.currentHeadCardIndex -= 1
210+
self.visibleCardQueue.insert(self.previousHeadCard!, atIndex: 0)
211+
self.layoutVisibleCardViews()
212+
})
213+
191214
}
192215

193216
func reloadData(){
@@ -277,7 +300,7 @@ class UICardContainerView: UIView {
277300

278301
private func generateNewCardView() -> UICardView{
279302
let newCardView = UICardView(frame: CGRect(origin: CGPointZero, size: defaultCardSize))
280-
303+
newCardView.layer.borderColor = UIColor.whiteColor().CGColor
281304
addSubview(newCardView)
282305
addConstraint(NSLayoutConstraint(item: newCardView, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1, constant: 0))
283306

@@ -329,6 +352,7 @@ class UICardContainerView: UIView {
329352

330353
private func resetCardViewtoReuse(cardView: UICardView){
331354
adjustConstraintOfCardView(cardView, withTargetIndex: 0)
355+
cardView.layer.borderWidth = 0
332356
cardView.layer.zPosition = CGFloat(101)
333357
cardView.hiddenContent()
334358

@@ -341,6 +365,12 @@ class UICardContainerView: UIView {
341365
/*Adjust CardView's constraint to right location*/
342366
private func adjustConstraintOfCardView(cardView: UICardView, withTargetIndex index: Int){
343367
cardView.layer.zPosition = CGFloat(100 - index)
368+
if needsBorder{
369+
cardView.layer.borderWidth = calculateBorderWidthForIndex(index, initialBorderWidth: headCardBorderWidth)
370+
}else{
371+
cardView.layer.borderWidth = 0
372+
}
373+
344374
if enableBrightnessControl{
345375
cardView.adjustAlpha(CGFloat(index) / CGFloat(5))
346376
}
@@ -460,8 +490,12 @@ class UICardContainerView: UIView {
460490
headCard?.layer.transform = flipTransform3D
461491
if percent >= 0.5{
462492
headCard?.hiddenContent()
493+
headCard?.layer.borderWidth = 0
463494
}else{
464495
headCard?.restoreContent()
496+
if needsBorder{
497+
headCard?.layer.borderWidth = headCardBorderWidth
498+
}
465499
}
466500
case 1.0...CGFloat(MAXFLOAT):
467501
flipTransform3D = CATransform3DRotate(flipTransform3D, CGFloat(-M_PI), 1, 0, 0)
@@ -475,8 +509,12 @@ class UICardContainerView: UIView {
475509
previousHeadCard?.layer.transform = flipTransform3D
476510
if percent <= -0.5{
477511
previousHeadCard?.restoreContent()
512+
if needsBorder{
513+
previousHeadCard?.layer.borderWidth = headCardBorderWidth
514+
}
478515
}else{
479516
previousHeadCard?.hiddenContent()
517+
previousHeadCard?.layer.borderWidth = 0
480518
}
481519
default: break
482520
}
@@ -502,12 +540,19 @@ class UICardContainerView: UIView {
502540
})
503541
}else{
504542
headCard?.restoreContent()
543+
if needsBorder{
544+
headCard?.layer.borderWidth = headCardBorderWidth
545+
}
505546
UIView.animateWithDuration(0.2, animations: {
506547
headCard?.layer.transform = CATransform3DIdentity
507548
})
508549
}
509550
}else{
510551
if percent <= -0.5{
552+
if needsBorder{
553+
previousHeadCard?.layer.borderWidth = headCardBorderWidth
554+
}
555+
511556
UIView.animateWithDuration(0.2, animations: {
512557
self.previousHeadCard?.layer.transform = CATransform3DIdentity
513558
}, completion: {_ in

Demo/CardAnimation/Base.lproj/Main.storyboard

+5-3
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,23 @@
4242
</connections>
4343
</button>
4444
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q3k-qd-ZGT">
45-
<rect key="frame" x="522" y="285" width="40" height="30"/>
45+
<rect key="frame" x="499" y="279" width="63" height="42"/>
46+
<fontDescription key="fontDescription" type="system" pointSize="25"/>
4647
<state key="normal" title="Insert"/>
4748
<connections>
4849
<action selector="insertACard:" destination="BYZ-38-t0r" eventType="touchUpInside" id="Kes-kn-yvi"/>
4950
</connections>
5051
</button>
5152
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CGa-Eq-3CB">
52-
<rect key="frame" x="20" y="285" width="45" height="30"/>
53+
<rect key="frame" x="20" y="279" width="72" height="42"/>
54+
<fontDescription key="fontDescription" type="system" pointSize="25"/>
5355
<state key="normal" title="Delete"/>
5456
<connections>
5557
<action selector="deleteACard:" destination="BYZ-38-t0r" eventType="touchUpInside" id="2We-2u-TF2"/>
5658
</connections>
5759
</button>
5860
</subviews>
59-
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
61+
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
6062
<constraints>
6163
<constraint firstItem="q3k-qd-ZGT" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="5no-FK-Tay"/>
6264
<constraint firstAttribute="leadingMargin" secondItem="CGa-Eq-3CB" secondAttribute="leading" id="IUq-59-mFL"/>

Demo/CardAnimation/ViewController.swift

+1-30
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ class ViewController: UIViewController, CardContainerDataSource {
3939
override func viewDidLoad() {
4040
super.viewDidLoad()
4141

42-
cardContainerView.translatesAutoresizingMaskIntoConstraints = false
43-
cardContainerView.backgroundColor = UIColor.blueColor()
42+
cardContainerView.clipsToBounds = false
4443
view.addSubview(cardContainerView)
4544
view.addConstraint(NSLayoutConstraint(item: cardContainerView, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1, constant: 0))
4645
view.addConstraint(NSLayoutConstraint(item: cardContainerView, attribute: .CenterY, relatedBy: .Equal, toItem: self.view, attribute: .CenterY, multiplier: 1, constant: 0))
@@ -65,34 +64,6 @@ class ViewController: UIViewController, CardContainerDataSource {
6564
}
6665

6766
@IBAction func flipDown(sender: AnyObject) {
68-
// guard let frontView = view.viewWithTag(frontCardTag) else{
69-
// return
70-
// }
71-
//
72-
//
73-
// let duration: NSTimeInterval = 0.5
74-
// //adjust borderWidth. Because the animation of borderWidth change in keyFrame animation can't work, so place it in dispatch_after
75-
// //本来 layer 的 borderWidth 是个可以动画的属性,但是在 UIView Animation 却不工作,没办法,只能用这种方式了
76-
// let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(duration * Double(NSEC_PER_SEC) / 2.0))
77-
// dispatch_after(delayTime, dispatch_get_main_queue(), {
78-
// frontView.layer.borderWidth = 0
79-
// })
80-
81-
82-
//There is a bug when you want to rotate 180, if you use UIView blcok animation, it doesn't work as expected: 1.no animation, just jump to final value; 2.rotate wrong direction.
83-
//You could use a closed value or animate it in UIView key frame animation.
84-
//此处有个很大的问题,折磨了我几个小时。官方的实现有Bug,在 UIView block animation 里旋转180度时会出现两种情况,一是不会执行动画而是直接跳到终点值,二是反方向旋转。
85-
//其他的角度没有问题,这里使用近似值替代不会产生这个个问题。不过, 在 key frame animation 里执行这个动画是正常的。
86-
// flipDownTransform3D = CATransform3DRotate(flipDownTransform3D, CGFloat(-M_PI)*0.99, 1, 0, 0)
87-
// UIView.animateWithDuration(duration, animations: {
88-
// frontView.layer.transform = flipDownTransform3D
89-
// })
90-
91-
//And in key frame animtion, we can fix another bug: a view is transparent in transform rotate.
92-
//I put the view which show the content in a container view, when the container view is vertical to screen, hide the nested content view, then we can see only the content of background color, just like the back of a card.
93-
//用 key frame animation 可以方便地解决卡片在旋转过程中背面透明的问题,解决办法是将内容视图放入一个容器视图,当容器视图旋转90时,此时将内容视图隐藏,从这时候开始我们就只能看到容器视图的背景色了,这样一来就和现实接近了。
94-
//而在普通的 UIView animation 里,在旋转一半的时候将内容视图隐藏比较麻烦,比如先旋转90度,在 completion block 里将内容视图隐藏,然后再添加一个动画继续旋转。用 key frame 里就比较方便,而且没有UIView animation 里旋转180有问题的 bug。
95-
9667
cardContainerView.slideDown()
9768
}
9869

README.md

+56-36
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,45 @@
22
[Design from Dribble](https://dribbble.com/shots/1265487-First-shot-in-Chapps-Animation). And a [blog](http://www.jianshu.com/p/286222d4edf8) for this, only chinese.
33
![Design from Dribble](https://d13yacurqjgara.cloudfront.net/users/32399/screenshots/1265487/attachments/173545/secret-project-animation_2x.gif)
44

5-
Thanks for [@luxorules](https://github.com/luxorules/CardAnimation/tree/Component)'s great work. Now you can use this animation in your project easily.
6-
7-
Features:
8-
- Custom card View size. (New added by @luxorules)
9-
- Custom card view, not only image view. (New added by @luxorules)
10-
- Support pan gesture.
11-
12-
**How to use it in your project**
13-
14-
Drag class files in the "Classes" folder into your project, includes CardAnimationView.swift and ImageCardView.swift.
5+
## API
6+
7+
I rewrite it. Support reuse card view and pan gesture. You just need to provide number of cards and relative image.
8+
9+
protocol CardContainerDataSource {
10+
func numberOfCardsForCardContainerView(cardContainerView: UICardContainerView) -> Int
11+
func cardContainerView(cardContainerView: UICardContainerView, imageForCardAtIndex: Int) -> UIImage?
12+
}
13+
14+
class UICardContainerView : UIView {
15+
var dataSource: CardContainerDataSource?
16+
17+
//If you want to custom these properties, configure them before asign dataSource, and don't change them once you custom them.
18+
//'needsCardCenterVertically' decide card is center vertically in container, or distance of bottom between card and contaienr is the height of card.
19+
var needsCardCenterVertically: Bool = false
20+
var enableBrightnessControl: Bool = true
21+
var maxVisibleCardCount: Int = 10
22+
var defaultCardSize: CGSize = CGSize(width: 400, height: 300)
23+
var needsBorder: Bool = true
24+
var headCardBorderWidth: CGFloat = 5
25+
26+
func slideDown()
27+
func slideUp()
28+
func reloadData()
29+
func insertCardAtIndex(toIndex: Int)
30+
func deleteCardAtIndex(toIndex: Int)
31+
//Call this method in viewDidLayoutSubviews()
32+
func respondsToSizeChange()
33+
}
34+
35+
Example:
36+
37+
let cardContainerView = UICardContainerView(frame: aFrame)
38+
aSuperView.addSubview(cardContainerView)
39+
cardContainerView.dataSource = id<CardContainerDataSource>
40+
41+
Done.
42+
43+
[@luxorules](https://github.com/luxorules/CardAnimation/tree/Component) packaged there code before, support pan gesture, card size and custom card view, not only image view. So there are two solutions for choice now. @luxorules's solution:
1544

1645
Classes:
1746

@@ -21,18 +50,15 @@ Classes:
2150

2251
You can custom animation behavior by set the below properties.
2352

24-
//Animation time for a single card animation.
25-
26-
`public var animationsSpeed = 0.2`
27-
28-
//Defines the card size that will be used. (width, height)
29-
30-
`public var cardSize : (width:CGFloat, height:CGFloat)`
53+
//Animation time for a single card animation.
54+
public var animationsSpeed = 0.2
55+
//Defines the card size that will be used. (width, height)
56+
public var cardSize : (width:CGFloat, height:CGFloat)
3157

3258
CardAnimationView needs a data source delegate to display the content, like UICollectionView.
3359

34-
`public weak var dataSourceDelegate : CardAnimationViewDataSource?`
35-
60+
public weak var dataSourceDelegate : CardAnimationViewDataSource?
61+
3662
protocol CardAnimationViewDataSource : class {
3763
func numberOfVisibleCards() -> Int
3864
func numberOfCards() -> Int
@@ -58,10 +84,15 @@ How to reuse a card view? There is an example in `ComponentExampleViewController
5884
retView!.imageView.image = UIImage.init(named: JusticeLeagueLogos.logoArray[number].rawValue)
5985
return retView!
6086
}
87+
## How to use?
6188

62-
**Techniques in the animation:**
89+
You can find two solutions in 'Classes' folder. Drag them in your project.
6390

64-
**Change Anchor Point of CALayer**
91+
92+
93+
## Technical Q&A
94+
95+
#### Change Anchor Point of CALayer
6596

6697
like: (0.5, 0.5) ->(0.5, 1)
6798

@@ -83,36 +114,25 @@ AutoLayout:
83114
//Like what you do with frame, you need to compensate for additional translation.
84115
centerYConstraint.constant = subView.bounds.size.height/2 + oldConstraintConstant
85116

86-
**Transform and AutoLayout**
117+
#### Transform and AutoLayout
87118

88119
From iOS8, transform and autolayout play nice. There is a blog for this: [Constraints & Transformations](http://revealapp.com/blog/constraints-and-transforms.html)
89120

90121
Transform doesn't affect autolayout, only constraints can affect autolayout.
91122

92123
Transform affects view's frame, but do nothing to view's center and bounds.
93124

94-
**Make flip animation background not transparent**
125+
#### Flip animation with non-transparent background
95126

96127
Use a subview, and change the container view's background color to what color you want.
97128

98129
When the container view is vertical to screen, make the subview hidden, and after the container view back, make subview visible.
99130

100-
**Rotation Animation Bug in action method**
131+
#### Rotation Animation Bug in action method
101132

102133
let flipTransform = CATransform3DRotate(CATransform3DIdentity, CGFloat(-M_PI), 1, 0, 0)
103134
UIView.animateWithDuration(0.3, {
104135
view.layer.transform = flipTransform
105136
})
106137

107-
The animation will not execute and the view just change if you execute above code in an action method, like clip a button.
108-
You could use 'CGFloat(-M_PI) * 0.99' to fix this.
109-
110-
Or, use UIView key frame animation, this transform works fine in key frame animation.
111-
112-
**To-Do List**
113-
114-
~~1.reuse card view~~
115-
116-
2.reorder card view
117-
118-
3.delete and add card view with pan gesture
138+
The animation will not execute and the view just change if you execute above code in an action method, like clip a button. UIView keyFrame animation works fine.

0 commit comments

Comments
 (0)