图片的翻页效果
在需要显示的控制器里写。
- ( void )viewDidLoad {
[superviewDidLoad]; //To change the template use AppCode | Preferences | File Templates.
self.view.backgroundColor = [UIColorlightGrayColor];
self.view.frame = CGRectMake(0, 0, 320, 460);
NSMutableArray *sourceImages = [NSMutableArrayarrayWithCapacity:20];
for (int i = 0; i <20 ; i++) {
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"%d.jpg", i]];
[sourceImages addObject:image];
}
//CoverFlowView *coverFlowView = [CoverFlowView coverFlowViewWithFrame: frame andImages:_arrImages sidePieces:6 sideScale:0.35 middleScale:0.6];
// CoverFlowView *coverFlowView = [CoverFlowView coverFlowViewWithFrame:self.view.frame andImages:sourceImages sideImageCount:6 sideImageScale:0.35 middleImageScale:0.6];
CoverFlowView *coverFlowView = [CoverFlowViewcoverFlowViewWithFrame:CGRectMake(0, 0, 320, 200) andImages:sourceImages sideImageCount:6sideImageScale:0.35middleImageScale:0.6];
[self.view addSubview:coverFlowView];
}
自定义个uiview
@interface CoverFlowView : UIView
//setup numbers of images
@property (nonatomic) int sideVisibleImageCount;
//setup the scale of left/right side and middle one
@property (nonatomic) CGFloat sideVisibleImageScale;
@property (nonatomic) CGFloat middleImageScale;
//source images
@property (nonatomic, retain) NSMutableArray *images;
//images layers, to help remove previous sublayer
@property (nonatomic, retain) NSMutableArray *imageLayers;
//template layers , to pre-locate every geometry info of layer
@property (nonatomic, retain) NSMutableArray *templateLayers;
//index in images for image rendering in the middle of cover flow
@property (nonatomic) int currentRenderingImageIndex;
//show the progress of browser : pagecontrol
@property (nonatomic, retain) UIPageControl *pageControl;
//factory method
+ (id)coverFlowViewWithFrame:(CGRect)frame
andImages: (NSMutableArray *)rawImages
sideImageCount:(int) sideCount
sideImageScale: (CGFloat) sideImageScale
middleImageScale: (CGFloat) middleImageScale;
//get index for current image that in the middle in images
- (int)getIndexForMiddle;
@end
#import "CoverFlowView.h"
#import <QuartzCore/QuartzCore.h>
#import <CoreGraphics/CoreGraphics.h>
#define DISTNACE_TO_MAKE_MOVE_FOR_SWIPE 60
@interfaceCoverFlowView ()
//setup templates
-(void)setupTemplateLayers;
//setup images
-(void)setupImages;
//remove sublayers (after a certain delay)
-(void)removeLayersAfterSeconds:(id)layerToBeRemoved;
//remove all sublayers
-(void)removeSublayers;
//empty imagelayers
-(void)cleanImageLayers;
//add reflections
-(void)showImageAndReflection:(CALayer *)layer;
//adjust the bounds
-(void)scaleBounds: (CALayer *) layer x:(CGFloat)scaleWidth y:(CGFloat)scaleHeight;
//add uipagecontrol
-(void)addPageControl;
@end
@implementation CoverFlowView {
@private
NSMutableArray *_images;
NSMutableArray *_imageLayers;
NSMutableArray *_templateLayers;
int _currentRenderingImageIndex;
UIPageControl *_pageControl;
int _sideVisibleImageCount;
CGFloat _sideVisibleImageScale;
CGFloat _middleImageScale;
}
@synthesize images = _images;
@synthesize imageLayers = _imageLayers;
@synthesize templateLayers = _templateLayers;
@synthesize currentRenderingImageIndex = _currentRenderingImageIndex;
@synthesize pageControl = _pageControl;
@synthesize sideVisibleImageCount = _sideVisibleImageCount;
@synthesize sideVisibleImageScale = _sideVisibleImageScale;
@synthesize middleImageScale = _middleImageScale;
- (void)adjustReflectionBounds:(CALayer *)layer scale:(CGFloat)scale {
// set originLayer's reflection bounds
CALayer *reflectLayer = (CALayer*)[layer.sublayers objectAtIndex:0];
[self scaleBounds:reflectLayer x:scale y:scale];
// set originLayer's reflection bounds
[self scaleBounds:reflectLayer.mask x:scale y:scale];
// set originLayer's reflection bounds
[self scaleBounds:(CALayer*)[reflectLayer.sublayers objectAtIndex:0] x:scale y:scale];
// set originLayer's reflection position
reflectLayer.position = CGPointMake(layer.bounds.size.width/2, layer.bounds.size.height*1.5);
// set originLayer's mask position
reflectLayer.mask.position = CGPointMake(reflectLayer.bounds.size.width/2, reflectLayer.bounds.size.height/2);
// set originLayer's reflection position
((CALayer*)[reflectLayer.sublayers objectAtIndex:0]).position = CGPointMake(reflectLayer.bounds.size.width/2, reflectLayer.bounds.size.height/2);
}
- (void)moveOneStep:(BOOL)isSwipingToLeftDirection {
//when move the first/last image,disable moving
if ((self.currentRenderingImageIndex == 0 && !isSwipingToLeftDirection) || (self.currentRenderingImageIndex == self.images.count -1 && isSwipingToLeftDirection))
return;
int offset = isSwipingToLeftDirection ? -1 : 1;
int indexOffsetFromImageLayersToTemplates = (self.currentRenderingImageIndex - self.sideVisibleImageCount < 0) ? (self.sideVisibleImageCount + 1 + offset - self.currentRenderingImageIndex) : 1 + offset;
for (int i = 0; i < self.imageLayers.count; i++) {
CALayer *originLayer = [self.imageLayers objectAtIndex:i];
CALayer *targetTemplate = [self.templateLayers objectAtIndex: i + indexOffsetFromImageLayersToTemplates];
[CATransactionsetAnimationDuration:1];
originLayer.position = targetTemplate.position;
originLayer.zPosition = targetTemplate.zPosition;
originLayer.transform = targetTemplate.transform;
//set originlayer's bounds
CGFloat scale = 1.0f;
if (i + indexOffsetFromImageLayersToTemplates - 1 == self.sideVisibleImageCount) {
scale = self.middleImageScale / self.sideVisibleImageScale;
} else if (((i + indexOffsetFromImageLayersToTemplates - 1 == self.sideVisibleImageCount - 1) && isSwipingToLeftDirection) ||
((i + indexOffsetFromImageLayersToTemplates - 1 == self.sideVisibleImageCount + 1) && !isSwipingToLeftDirection)) {
scale = self.sideVisibleImageScale / self.middleImageScale;
}
originLayer.bounds = CGRectMake(0, 0, originLayer.bounds.size.width * scale, originLayer.bounds.size.height * scale);
[self adjustReflectionBounds:originLayer scale:scale];
}
if (isSwipingToLeftDirection){
//when current rendering index >= sidecout
if(self.currentRenderingImageIndex >= self.sideVisibleImageCount){
CALayer *removeLayer = [self.imageLayers objectAtIndex:0];
[self.imageLayers removeObject:removeLayer];
[removeLayer removeFromSuperlayer];
}
int num = self.images.count - self.sideVisibleImageCount - 1;
if (self.currentRenderingImageIndex < num){
UIImage *candidateImage = [self.imagesobjectAtIndex:self.currentRenderingImageIndex + self.sideVisibleImageCount + 1];
CALayer *candidateLayer = [CALayer layer];
candidateLayer.contents = (__bridge id)candidateImage.CGImage;
CGFloat scale = self.sideVisibleImageScale;
candidateLayer.bounds = CGRectMake(0, 0, candidateImage.size.width * scale, candidateImage.size.height * scale);
[self.imageLayers addObject:candidateLayer];
CALayer *template = [self.templateLayers objectAtIndex:self.templateLayers.count - 2];
candidateLayer.position = template.position;
candidateLayer.zPosition = template.zPosition;
candidateLayer.transform = template.transform;
//show the layer
[self showImageAndReflection:candidateLayer];
}
}else{//if the right, then move the rightest layer and insert one to left (if left is full)
//when to remove rightest, only when image in the rightest is indeed sitting in the template imagelayer's rightes
if (self.currentRenderingImageIndex + self.sideVisibleImageCount <= self.images.count -1) {
CALayer *removeLayer = [self.imageLayers lastObject];
[self.imageLayers removeObject:removeLayer];
[removeLayer removeFromSuperlayer];
}
//check out whether we need to add layer to left, only when (currentIndex - sideCount > 0)
if (self.currentRenderingImageIndex > self.sideVisibleImageCount){
UIImage *candidateImage = [self.imagesobjectAtIndex:self.currentRenderingImageIndex - 1 - self.sideVisibleImageCount];
CALayer *candidateLayer = [CALayer layer];
candidateLayer.contents = (__bridge id)candidateImage.CGImage;
CGFloat scale = self.sideVisibleImageScale;
candidateLayer.bounds = CGRectMake(0, 0, candidateImage.size.width * scale, candidateImage.size.height * scale);
[self.imageLayers insertObject:candidateLayer atIndex:0];
CALayer *template = [self.templateLayers objectAtIndex:1];
candidateLayer.position = template.position;
candidateLayer.zPosition = template.zPosition;
candidateLayer.transform = template.transform;
//show the layer
[self showImageAndReflection:candidateLayer];
}
}
//update index if you move to right, index--
self.currentRenderingImageIndex = isSwipingToLeftDirection ? self.currentRenderingImageIndex + 1 : self.currentRenderingImageIndex - 1;
}
- (void)scaleBounds:(CALayer*)layer x:(CGFloat)scaleWidth y:(CGFloat)scaleHeight
{
layer.bounds = CGRectMake(0, 0, layer.bounds.size.width*scaleWidth, layer.bounds.size.height*scaleHeight);
}
- (void)handleGesture:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateChanged){
//get offset
CGPoint offset = [recognizer translationInView:recognizer.view];
if (abs(offset.x) > DISTNACE_TO_MAKE_MOVE_FOR_SWIPE) {
BOOL isSwipingToLeftDirection = (offset.x > 0) ? NO :YES;
[self moveOneStep:isSwipingToLeftDirection];
[recognizer setTranslation:CGPointZero inView:recognizer.view];
}
}
}
+ (id)coverFlowViewWithFrame:(CGRect)frame andImages:(NSMutableArray *)rawImages sideImageCount:(int)sideCount sideImageScale:(CGFloat)sideImageScale middleImageScale:(CGFloat)middleImageScale {
CoverFlowView *flowView = [[CoverFlowView alloc] initWithFrame:frame];
flowView.sideVisibleImageCount = sideCount;
flowView.sideVisibleImageScale = sideImageScale;
flowView.middleImageScale = middleImageScale;
//default set middle image to the first image in the source images array
flowView.currentRenderingImageIndex = 6;
flowView.images = [NSMutableArray arrayWithArray:rawImages];
flowView.imageLayers = [[NSMutableArray alloc] initWithCapacity:flowView.sideVisibleImageCount* 2 + 1];
flowView.templateLayers = [[NSMutableArray alloc] initWithCapacity:(flowView.sideVisibleImageCount + 1)* 2 + 1];
//register the pan gesture to figure out whether user has intention to move to next/previous image
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:flowView action:@selector(handleGesture:)];
[flowView addGestureRecognizer:gestureRecognizer];
//now almost setup
[flowView setupTemplateLayers];
[flowView setupImages];
[flowView addPageControl];
return flowView;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
//set up perspective
CATransform3D transformPerspective = CATransform3DIdentity;
transformPerspective.m34 = -1.0 / 500.0;
self.layer.sublayerTransform = transformPerspective;
}
returnself;
}
-(void)setupTemplateLayers {
CGFloat centerX = self.bounds.size.width/2;
CGFloat centerY = self.bounds.size.height/2;
//the angle to rotate
CGFloat leftRadian = M_PI/3;
CGFloat rightRadian = -M_PI/3;
//gap between images in side
CGFloat gapAmongSideImages = 30.0f;
//gap between middle one and neigbour(this word is so hard to type wrong: WTF)
CGFloat gapBetweenMiddleAndSide = 100.0f;
//setup the layer templates
//let's start from left side
for(int i = 0; i <= self.sideVisibleImageCount; i++){
CALayer *layer = [CALayer layer];
layer.position = CGPointMake(centerX - gapBetweenMiddleAndSide - gapAmongSideImages * (self.sideVisibleImageCount - i), centerY);
layer.zPosition = (i - self.sideVisibleImageCount - 1) * 10;
layer.transform = CATransform3DMakeRotation(leftRadian, 0, 1, 0);
[self.templateLayers addObject:layer];
}
//middle
CALayer *layer = [CALayer layer];
layer.position = CGPointMake(centerX, centerY);
[self.templateLayers addObject:layer];
//right
for(int i = 0; i <= self.sideVisibleImageCount; i++){
CALayer *layer = [CALayer layer];
layer.position = CGPointMake(centerX + gapBetweenMiddleAndSide + gapAmongSideImages * i, centerY);
layer.zPosition = (i + 1) * -10;
layer.transform = CATransform3DMakeRotation(rightRadian, 0, 1, 0);
[self.templateLayers addObject:layer];
}
}
- (void)setupImages {
// setup the visible area, and start index and end index
int startingImageIndex = (self.currentRenderingImageIndex - self.sideVisibleImageCount <= 0) ? 0 : self.currentRenderingImageIndex - self.sideVisibleImageCount;
int endImageIndex = (self.currentRenderingImageIndex + self.sideVisibleImageCount < self.images.count ) ? (self.currentRenderingImageIndex + self.sideVisibleImageCount) : (self.images.count -1 );
//step2: set up images that ready for rendering
for (int i = startingImageIndex; i <= endImageIndex; i++) {
UIImage *image = [self.images objectAtIndex:i];
CALayer *imageLayer = [CALayer layer];
imageLayer.contents = (__bridge id)image.CGImage;
CGFloat scale = (i == self.currentRenderingImageIndex) ? self.middleImageScale : self.sideVisibleImageScale;
imageLayer.bounds = CGRectMake(0, 0, image.size.width * scale, image.size.height*scale);
[self.imageLayers addObject:imageLayer];
}
//step3 : according to templates, set its geometry info to corresponding image layer
//1 means the extra layer in templates layer
//damn mathmatics
int indexOffsetFromImageLayersToTemplates = (self.currentRenderingImageIndex - self.sideVisibleImageCount < 0) ? (self.sideVisibleImageCount + 1 - self.currentRenderingImageIndex) : 1;
for (int i = 0; i < self.imageLayers.count; i++) {
CALayer *correspondingTemplateLayer = [self.templateLayers objectAtIndex:(i + indexOffsetFromImageLayersToTemplates)];
CALayer *imageLayer = [self.imageLayers objectAtIndex:i];
imageLayer.position = correspondingTemplateLayer.position;
imageLayer.zPosition = correspondingTemplateLayer.zPosition;
imageLayer.transform = correspondingTemplateLayer.transform;
//show its reflections
[self showImageAndReflection:imageLayer];
}
}
// 添加layer及其“倒影”
- (void)showImageAndReflection:(CALayer*)layer
{
// 制作reflection
CALayer *reflectLayer = [CALayer layer];
reflectLayer.contents = layer.contents;
reflectLayer.bounds = layer.bounds;
reflectLayer.position = CGPointMake(layer.bounds.size.width/2, layer.bounds.size.height*1.5);
reflectLayer.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
// 给该reflection加个半透明的layer
CALayer *blackLayer = [CALayer layer];
blackLayer.backgroundColor = [UIColorblackColor].CGColor;
blackLayer.bounds = reflectLayer.bounds;
blackLayer.position = CGPointMake(blackLayer.bounds.size.width/2, blackLayer.bounds.size.height/2);
blackLayer.opacity = 0.6;
[reflectLayer addSublayer:blackLayer];
// 给该reflection加个mask
CAGradientLayer *mask = [CAGradientLayerlayer];
mask.bounds = reflectLayer.bounds;
mask.position = CGPointMake(mask.bounds.size.width/2, mask.bounds.size.height/2);
mask.colors = [NSArrayarrayWithObjects:
(__bridge id)[UIColor clearColor].CGColor,
(__bridge id)[UIColor whiteColor].CGColor, nil];
mask.startPoint = CGPointMake(0.5, 0.35);
mask.endPoint = CGPointMake(0.5, 1.0);
reflectLayer.mask = mask;
// 作为layer的sublayer
[layer addSublayer:reflectLayer];
// 加入UICoverFlowView的sublayers
[self.layer addSublayer:layer];
}
- (void)addPageControl {
}
- (int)getIndexForMiddle {
return 0;
}
@end