上一期动画特效,我讲解了《图片翻转》。这一节,我们继续动画之旅,讲解一下图片折叠效果。
图片折叠:顾名思义就是绕着图片的x或者y轴进行折叠,并且看上去有透视效果。先看看最终的效果图:
编程思路分析:
1. 使用一张图片 (Failed) 因为进行图片旋转的时候,如果下半部分的图片往前翻转了,上半部分的图片就会同时往后翻转了。
2. 使用两张图片 (Failed) 通过控制图片的显示与隐藏,但是折叠效果看不出来。
3. 使用一个View(它的大小保持和传递进来的图片大小一致),两张图片,而这两张图片是通过代码裁切,将一张图片裁切为上下两部分,然后分别放置在View的上半部分和下半部分,看起来好像是一张图片(Succeed)。
自定义FoldingView
@interface FoldingView : UIView - (instancetype)initWithImage:(UIImage *)image; @end
- (instancetype)initWithImage:(UIImage *)image { if (self = [super init]) { self.image = image; // 父View的尺寸等于传递过来的图片的尺寸 self.frame = CGRectMake(0, 0, image.size.width, image.size.height); [self addTopView]; [self addBottomView]; [self addGestures]; } return self; }
二、addTopView用来裁切图片的上半部分并且放置在FoldingView的上面。
- (void)addTopView { UIImage *topImage = [self cutImageWithOrignalImage:self.image type:LayerSectionTop]; // 设置图片的frame是父View的frame一半 self.topImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetMidY(self.bounds))]; self.topImageView.userInteractionEnabled = YES; self.topImageView.image = topImage; // 设置内容模式为ScaleAspectFit,图片不拉伸 self.topImageView.contentMode = UIViewContentModeScaleAspectFit; // 下面设置的position和anchorPoint不写,其实图片已经显示在父控件上面的一半了,但是动画操作的layer是要针对position和anchorPoint的,所以设置他们!!! self.topImageView.layer.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); self.topImageView.layer.anchorPoint = CGPointMake(0.5, 1.0); self.topImageView.layer.mask = [self maskForSection:LayerSectionTop withRect:self.topImageView.bounds]; //self.topImageView.layer.transform = CATransform3DMakePerspective(CGPointZero); self.topImageView.layer.transform = [self transform3D]; [self addSubview:self.topImageView]; }
typedef NS_ENUM(NSInteger, LayerSection){ LayerSectionTop, LayerSectionBottom };
// 裁切图片的上面一半或者下面一半 - (UIImage *)cutImageWithOrignalImage:(UIImage *)image type:(LayerSection)section { CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height * 0.5); if (section == LayerSectionBottom) { rect.origin.y = image.size.height * 0.5; } CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, rect); UIImage *finalImage = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); return finalImage; }
然后,我们接着看第二点的代码;有两点我需要说明:1.position和anchorPoint的使用(如果想了解具体使用,请参照我以前写的博客《position和anchorPoint》); 2. 一个View设置部分圆角效果(《Core Animation高级理论知识汇总》中的专用图层有介绍过)。其实,我们完全可以通过设置被裁切图片的frame来控制图片在FoldingView中的显示位置来达到效果,之所以设置position和anchorPoint就是为了后面的折叠效果做准备。下面一张图说明了折叠原理。
四、添加手势
- (void)addGestures { UIPanGestureRecognizer *topPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panTopEvent:)]; [self.topImageView addGestureRecognizer:topPan]; } - (void)panTopEvent:(UIPanGestureRecognizer *)recognizer { CGPoint location = [recognizer locationInView:self]; if (recognizer.state == UIGestureRecognizerStateBegan) { self.initialLocation = location.y; [self bringSubviewToFront:self.topImageView]; } // 上面一半的图片只允许往下拉 CGFloat delta = location.y - self.initialLocation; if (delta < 0) return; CGFloat conversionFactor = -M_PI / CGRectGetHeight(self.bounds); CGFloat angle = delta * conversionFactor; if (angle <= -M_PI) { angle = -M_PI; } self.topImageView.layer.transform = CATransform3DConcat(CATransform3DMakeRotation(angle, 1, 0, 0), [self transform3D]); if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) { [UIView animateWithDuration:0.25 animations:^{ self.topImageView.layer.transform = [self transform3D]; } completion:^(BOOL finished) { //[self shakeTop]; }]; } }
// 透视效果 - (CATransform3D)transform3D { CATransform3D transform = CATransform3DIdentity; transform.m34 = kPerspective; return transform; }
至此,上半部分图片折叠效果已经实现了,而下半部分很相似,我就不再说明了。