前言
為什么我們要做這個效果裝逼炫技??沒有別的了,廢話少說先看看效果
下方的箭頭主要是來確定翅楼,整個控件的高度尉剩,是隨著折疊而變化的。
分析
1毅臊、首先遇到問題還是先分析理茎。整個控件是一個折疊容器,然后容器里有很多個小的可以折疊的控件褂微,小的折疊控件,折疊的時候我們把錨點移到y(tǒng)=0园爷,然后在旋轉(zhuǎn)180°
2宠蚂、在每一個可旋轉(zhuǎn)的控件我們首先要做的是截圖,把整個一個可能很復雜的控件變?yōu)橐粡?code>imageview這樣方便我們待會做旋轉(zhuǎn)童社。
3求厕、要做出“翻過來”的效果我這里是使用一個模糊效果的
imageview
加載上面,旋轉(zhuǎn)180°的過程中并不是一次性完成的扰楼,因為我們?nèi)绻峭险郫B呀癣,旋轉(zhuǎn)到90°的時候我們應該是看到控件的背部,所以我們在旋轉(zhuǎn)90°動畫完成的后弦赖,要把“背部”放到最前面项栏,看上去就像翻過去了。大致的分析就到這兒蹬竖,其中還有很多細節(jié)需要處理
準備
- 第一步我們要配置好折疊容器中的可折疊元素沼沈,(這個demo中的0、1币厕、2列另、3、4)
/**
配置折疊元素
*/
- (void)configurationFoldItem {
self.unfoldArrayM = [NSMutableArray arrayWithCapacity:self.itemCount];
for (int i = 0; i < self.itemCount; i ++ ) {
CGRect rect = CGRectMake(0 , i * self.itemHeight, self.itemWidth, self.itemHeight);
UIImage *image = [self ai_takeSnapshotWithFrame:rect];
AIFoldRotatedView *rotatedView = [[AIFoldRotatedView alloc]initWithFrame:rect Image:image];
// rotatedView.layer.anchorPoint = CGPointMake(.5, 0);
// //測試
// rotatedView.tag = i+100;
rotatedView.delegate = self;
[self addSubview:rotatedView];
//添加到可折疊數(shù)組中
[self.itemArrayM addObject:rotatedView];
//保存到展開數(shù)組中
[self.unfoldArrayM addObject:[NSValue valueWithCGRect:rect]];
}
self.contentView.alpha = 0.;
}
實現(xiàn)
在自定義一個控件的時候我們不要立馬去實現(xiàn)某一個方法旦装,而是先想一下我們需要什么屬性页衙,需要什么方法,每一個方法需要哪些對應的參數(shù)阴绢。我覺得這才是一個面向?qū)ο缶幊痰乃枷搿?br>
我們需要讓旋轉(zhuǎn)控件做展開或者折疊的效果店乐,所以我們要有展開和折疊的方法,展開是要時間的,所以參數(shù)有duration
呻袭,我們的折疊容器中可折疊的控件不止一個要做到一個控件折疊完成后再折疊第二個所以我們有參數(shù)delay
/**
旋轉(zhuǎn)180度
@param duration 持續(xù)時長
@param delay 延時
*/
- (void)foldingAnimationMI_PWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay;
/**
展開旋轉(zhuǎn)180度
@param duration 持續(xù)時長
@param delay 延時
*/
- (void)unfoldingAnimationMI_PWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay;
在將要折疊和折疊完成后响巢,我們需要做一些事情,比如修改折疊容器的高度或者說修改狀態(tài)棒妨,我這里是使用協(xié)議代理實現(xiàn)的
@protocol AIFoldRotatedViewDelegate<NSObject>
/**
折疊完成后回調(diào)
@param roatatedView 折疊控件
@param anim anim description
@param flag flag description
*/
- (void)foldRotatedView:(AIFoldRotatedView*)roatatedView animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
/**
展開完成后回調(diào)
@param roatatedView 折疊控件
@param anim anim description
@param flag flag description
*/
- (void)unfoldRotatedView:(AIFoldRotatedView*)roatatedView animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
/**
將要折疊回調(diào)
@param roatatedView 折疊控件
*/
- (void)willfoldRotatedView:(AIFoldRotatedView*)roatatedView;
/**
將要展開回調(diào)
@param roatatedView 折疊控件
*/
- (void)willUnfoldRotatedView:(AIFoldRotatedView*)roatatedView;
@end
在我們剛才大致的分析了之后踪古,現(xiàn)在我們看如何實現(xiàn)這些方法
其中最最核心的就是我們要把這個控件旋轉(zhuǎn)一個角度含长,不管是展開還是折疊都是旋轉(zhuǎn)動畫所以我旋轉(zhuǎn)動畫封裝成一個方法
/**
旋轉(zhuǎn)動畫
@param timing 節(jié)奏
@param from 開始
@param to 結束
@param duration 持續(xù)時長
@param delay 延時
*/
- (CABasicAnimation*)rotationAnimationTiming:(NSString *)timing from:(CGFloat)from to:(CGFloat)to duration:(NSTimeInterval)duration delay:(NSTimeInterval)delay {
CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
rotateAnimation.timingFunction = [CAMediaTimingFunction functionWithName:timing];
rotateAnimation.fromValue = @(from);
rotateAnimation.toValue = @(to);
rotateAnimation.duration = duration;
rotateAnimation.delegate = self;
rotateAnimation.fillMode = kCAFillModeForwards;
rotateAnimation.removedOnCompletion = NO;
rotateAnimation.beginTime = CACurrentMediaTime() + delay;
return rotateAnimation;
}
接下來我以折疊為例,展開的方法和折疊類似
/**
折疊旋轉(zhuǎn)180度
@param duration 持續(xù)時長
@param delay 延時
*/
- (void)foldingAnimationMI_PWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay {
[self clearTransform];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.layer.position = CGPointMake(CGRectGetMidX(self.frame), self.ai_y );
self.layer.anchorPoint = CGPointMake(.5, 0);
if (self.delegate && [self.delegate respondsToSelector:@selector(willfoldRotatedView:)]) {
[self.delegate willfoldRotatedView:self ];
}else {
AILog(@"未設置代理");
}
});
CABasicAnimation *animation1Layer = [self rotationAnimationTiming:kCAMediaTimingFunctionEaseIn from:0 to:M_PI_2 duration:duration * .5 delay:delay ];
[animation1Layer setValue:@"foldstarAnimation" forKey:@"name"];
[self.layer addAnimation:animation1Layer forKey:@"animation1"];
}
1伏穆、大家可以看到我這里并沒有旋轉(zhuǎn)180°拘泞,而是旋轉(zhuǎn)的90°這是因為我在前面講的我們要實現(xiàn)折疊到一半,我們應該看到的是控件的"背面"枕扫,這里我動畫拿到了添加了key/value就是為了確定動畫結束的地方(這個技巧我在下載按鈕|動畫中也有使用)
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
NSString *name = [anim valueForKey:@"name"];
if ([name isEqualToString:@"foldstarAnimation"]) {//折疊到90°
// 讓backView到最前面來
[self bringSubviewToFront:self.backView];
CABasicAnimation *foldendAnimation = [self rotationAnimationTiming:kCAMediaTimingFunctionEaseOut from:M_PI_2 to:M_PI duration:anim.duration delay:0 ];
[foldendAnimation setValue:@"foldendAnimation" forKey:@"name"];
[self.layer addAnimation:foldendAnimation forKey:nil];
}else if([name isEqualToString:@"foldendAnimation"]){ //折疊完成
// [self.layer removeAllAnimations];
[self rotatedXWithAngle:0];
[self clearTransform];
if (self.delegate && [self.delegate respondsToSelector:@selector(foldRotatedView:animationDidStop:finished:)]) {
[self.delegate foldRotatedView:self animationDidStop:anim finished:flag];
}else {
AILog(@"未設置代理");
}
}
}
當折疊到90°的時候我們把背景圖陪腌,放到前面來,形成一個"看到控件背后的效果"烟瞧,整個180°旋轉(zhuǎn)完成后調(diào)用折疊完成的回調(diào)函數(shù)诗鸭,這里有點要注意,我們要清理layer
的transform
因為我們是有多個可旋轉(zhuǎn)的控件参滴,還原transform
在下一個控件旋轉(zhuǎn)的時候錨點就可能出問題强岸,這個特別是在展開的時候體現(xiàn)非常明顯
2、一個控件折疊完成后,需要修改的他的frame和把已經(jīng)折疊的這個控件放在砾赔,讓已經(jīng)折疊這個控件成為上一個控件的子控件
最后折疊完成后層級關系應該是這樣
/**
一個疊完成后回調(diào)
@param roatatedView 折疊視圖
@param anim anim description
@param flag flag description
*/
-(void)foldRotatedView:(AIFoldRotatedView *)roatatedView animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
[roatatedView.layer removeAllAnimations];
NSInteger index = [self.itemArrayM indexOfObject:roatatedView];
self.descView = self.itemArrayM[index-1];
roatatedView.frame = self.descView.bounds;
[self.descView addSubview:roatatedView];
}
GitHub上查看源碼蝌箍,你的star是我最大的支持
參考文獻
參考開源庫:popping
folding-cell
參考博客:http://blog.sina.com.cn/s/blog_8f5097be0101b91z.html