如何在ios中创建垂直翻转视图动画?

时间:2013-06-24 09:29:44

标签: ios flip flipboard page-flipping

我不知道如何垂直翻转我的uiview,我有很多视图和我的每个视图

有一些照片和他们的描述,我想要像书一样的翻转视图而不是

从左到右,它必须是从上到下或从下到上,

我想整个页面从上到下或从下到上翻转,

如何在ios中完成这类工作?

我正在谷歌上搜索但这不起作用我是开发中的新手

所以请任何人都可以正确地指导我如何翻转我的意见请帮助我

提前致谢。

3 个答案:

答案 0 :(得分:6)

https://github.com/mtabini/AFKPageFlipper

下载代码

并更改AFKPageFlipper.h和AFKPageFlipper.m文件

//
//  AFKPageFlipper.h
//  AFKPageFlipper
//
//  Created by Marco Tabini on 10-10-11.
//  Copyright 2010 AFK Studio Partnership. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>


@class AFKPageFlipper;


@protocol AFKPageFlipperDataSource

- (NSInteger) numberOfPagesForPageFlipper:(AFKPageFlipper *) pageFlipper;
- (UIView *) viewForPage:(NSInteger) page inFlipper:(AFKPageFlipper *) pageFlipper;

@end


typedef enum {
    AFKPageFlipperDirectionTop,
    AFKPageFlipperDirectionBottom,
} AFKPageFlipperDirection;



@interface AFKPageFlipper : UIView {
    NSObject <AFKPageFlipperDataSource> *dataSource;
    NSInteger currentPage;
    NSInteger numberOfPages;

    UIView *currentView;
    UIView *nextView;

    CALayer *backgroundAnimationLayer;
    CALayer *flipAnimationLayer;

    AFKPageFlipperDirection flipDirection;
    float startFlipAngle;
    float endFlipAngle;
    float currentAngle;

    BOOL setNextViewOnCompletion;
    BOOL animating;

    BOOL disabled;
}

@property (nonatomic,retain) NSObject <AFKPageFlipperDataSource> *dataSource;
@property (nonatomic,assign) NSInteger currentPage;

@property (nonatomic, retain) UITapGestureRecognizer *tapRecognizer;
@property (nonatomic, retain) UIPanGestureRecognizer *panRecognizer;

@property (nonatomic,assign) BOOL disabled;

- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated;

@end







//
//  AFKPageFlipper.m
//  AFKPageFlipper
//
//  Created by Marco Tabini on 10-10-12.
//  Copyright 2010 AFK Studio Partnership. All rights reserved.
//

#import "AFKPageFlipper.h"



#pragma mark -
#pragma mark UIView helpers


@interface UIView(Extended) 

- (UIImage *) imageByRenderingView;

@end


@implementation UIView(Extended)


- (UIImage *) imageByRenderingView {
    CGFloat oldAlpha = self.alpha;
    self.alpha = 1;
    UIGraphicsBeginImageContext(self.bounds.size);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    self.alpha = oldAlpha;
    return resultingImage;
}

@end


#pragma mark -
#pragma mark Private interface


@interface AFKPageFlipper()

@property (nonatomic,assign) UIView *currentView;
@property (nonatomic,assign) UIView *nextView;

@end


@implementation AFKPageFlipper

@synthesize tapRecognizer = _tapRecognizer;
@synthesize panRecognizer = _panRecognizer;


#pragma mark -
#pragma mark Flip functionality


- (void) initFlip {

    // Create screenshots of view

    UIImage *currentImage = [self.currentView imageByRenderingView];
    UIImage *newImage = [self.nextView imageByRenderingView];

    // Hide existing views

    self.currentView.alpha = 0;
    self.nextView.alpha = 0;

    // Create representational layers

    CGRect rect = self.bounds;
    rect.size.height /= 2;

    backgroundAnimationLayer = [CALayer layer];
    backgroundAnimationLayer.frame = self.bounds;
    backgroundAnimationLayer.zPosition = -300000;

    CALayer *topLayer = [CALayer layer];
    topLayer.frame = rect;
    topLayer.masksToBounds = YES;
    topLayer.contentsGravity = kCAGravityBottom;

    [backgroundAnimationLayer addSublayer:topLayer];

    rect.origin.y = rect.size.height;

    CALayer *bottomLayer = [CALayer layer];
    bottomLayer.frame = rect;
    bottomLayer.masksToBounds = YES;
    bottomLayer.contentsGravity = kCAGravityTop;

    [backgroundAnimationLayer addSublayer:bottomLayer];

    if (flipDirection == AFKPageFlipperDirectionBottom) {
        topLayer.contents = (id) [newImage CGImage];
        bottomLayer.contents = (id) [currentImage CGImage];
    } else {
        topLayer.contents = (id) [currentImage CGImage];
        bottomLayer.contents = (id) [newImage CGImage];
    }

    [self.layer addSublayer:backgroundAnimationLayer];

    rect.origin.y = 0;

    flipAnimationLayer = [CATransformLayer layer];
    flipAnimationLayer.anchorPoint = CGPointMake(0.5, 1);
    flipAnimationLayer.frame = rect;

    [self.layer addSublayer:flipAnimationLayer];

    CALayer *backLayer = [CALayer layer];
    backLayer.frame = flipAnimationLayer.bounds;
    backLayer.doubleSided = NO;
    backLayer.masksToBounds = YES;

    [flipAnimationLayer addSublayer:backLayer];

    CALayer *frontLayer = [CALayer layer];
    frontLayer.frame = flipAnimationLayer.bounds;
    frontLayer.doubleSided = NO;
    frontLayer.masksToBounds = YES;

    frontLayer.transform = CATransform3DMakeRotation(M_PI, 1.0, 0.0, 0);

    [flipAnimationLayer addSublayer:frontLayer];

    if (flipDirection == AFKPageFlipperDirectionBottom) {
        backLayer.contents = (id) [currentImage CGImage];
        backLayer.contentsGravity = kCAGravityBottom;

        frontLayer.contents = (id) [newImage CGImage];
        frontLayer.contentsGravity = kCAGravityTop;

        CATransform3D transform = CATransform3DMakeRotation(1.1/M_PI, 1.0, 0.0, 0.0);
        transform.m34 = 1.0f / 2500.0f;

        flipAnimationLayer.transform = transform;

        currentAngle = startFlipAngle = 0;
        endFlipAngle = M_PI;
    } else {
        //down
        backLayer.contents = (id) [newImage CGImage];
        backLayer.contentsGravity = kCAGravityBottom;

        frontLayer.contents = (id) [currentImage CGImage];
        frontLayer.contentsGravity = kCAGravityTop;

        CATransform3D transform = CATransform3DMakeRotation(M_PI/1.1, 1.0, 0.0, 0.0);
        transform.m34 = 1.0f / 2500.0f;

        flipAnimationLayer.transform = transform;

        currentAngle = startFlipAngle = M_PI;
        endFlipAngle = 0;
    }
}


- (void) cleanupFlip {
    [backgroundAnimationLayer removeFromSuperlayer];
    [flipAnimationLayer removeFromSuperlayer];

    backgroundAnimationLayer = Nil;
    flipAnimationLayer = Nil;

    animating = NO;

    if (setNextViewOnCompletion) {
        [self.currentView removeFromSuperview];
        self.currentView = self.nextView;
        self.nextView = Nil;
    } else {
        [self.nextView removeFromSuperview];
        self.nextView = Nil;
    }

    self.currentView.alpha = 1;
}


- (void) setFlipProgress:(float) progress setDelegate:(BOOL) setDelegate animate:(BOOL) animate {
    if (animate) {
        animating = YES;
    }

    float newAngle = startFlipAngle + progress * (endFlipAngle - startFlipAngle);

    float duration = animate ? 0.5 * fabs((newAngle - currentAngle) / (endFlipAngle - startFlipAngle)) : 0;

    currentAngle = newAngle;

    CATransform3D endTransform = CATransform3DIdentity;
    endTransform.m34 = 1.0f / 2500.0f;
    endTransform = CATransform3DRotate(endTransform, newAngle, 1.0, 0.0, 0.0);

    [flipAnimationLayer removeAllAnimations];

    [CATransaction begin];
    [CATransaction setAnimationDuration:duration];

    flipAnimationLayer.transform = endTransform;

    [CATransaction commit];

    if (setDelegate) {
        [self performSelector:@selector(cleanupFlip) withObject:Nil afterDelay:duration];
    }
}


- (void) flipPage {
    [self setFlipProgress:1.0 setDelegate:YES animate:YES];
}


#pragma mark -
#pragma mark Animation management


- (void)animationDidStop:(NSString *) animationID finished:(NSNumber *) finished context:(void *) context {
    [self cleanupFlip];
}


#pragma mark -
#pragma mark Properties

@synthesize currentView;


- (void) setCurrentView:(UIView *) value {
    if (currentView) {
        [currentView release];
    }

    currentView = [value retain];
}


@synthesize nextView;


- (void) setNextView:(UIView *) value {
    if (nextView) {
        [nextView release];
    }

    nextView = [value retain];
}


@synthesize currentPage;


- (BOOL) doSetCurrentPage:(NSInteger) value {
    if (value == currentPage) {
        return FALSE;
    }

    flipDirection = value < currentPage ? AFKPageFlipperDirectionBottom : AFKPageFlipperDirectionTop;

    currentPage = value;

    self.nextView = [self.dataSource viewForPage:value inFlipper:self];
    [self addSubview:self.nextView];

    return TRUE;
}   

- (void) setCurrentPage:(NSInteger) value {
    if (![self doSetCurrentPage:value]) {
        return;
    }

    setNextViewOnCompletion = YES;
    animating = YES;

    self.nextView.alpha = 0;

    [UIView beginAnimations:@"" context:Nil];
    [UIView setAnimationDuration:0.5];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    self.nextView.alpha = 1;

    [UIView commitAnimations];
} 


- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated {
    if (![self doSetCurrentPage:value]) {
        return;
    }

    setNextViewOnCompletion = YES;
    animating = YES;

    if (animated) {
        [self initFlip];
        [self performSelector:@selector(flipPage) withObject:Nil afterDelay:0.091];
    } else {
        [self animationDidStop:Nil finished:[NSNumber numberWithBool:NO] context:Nil];
    }

}


@synthesize dataSource;


- (void) setDataSource:(NSObject <AFKPageFlipperDataSource>*) value {
    if (dataSource) {
        [dataSource release];
    }

    dataSource = [value retain];
    numberOfPages = [dataSource numberOfPagesForPageFlipper:self];
    currentPage = 0;
    self.currentPage = 1;
}


@synthesize disabled;


- (void) setDisabled:(BOOL) value {
    disabled = value;

    self.userInteractionEnabled = !value;

    for (UIGestureRecognizer *recognizer in self.gestureRecognizers) {
        recognizer.enabled = !value;
    }
}


#pragma mark -
#pragma mark Touch management


- (void) tapped:(UITapGestureRecognizer *) recognizer {
    if (animating || self.disabled) {
        return;
    }

    if (recognizer.state == UIGestureRecognizerStateRecognized) {
        NSInteger newPage;

        if ([recognizer locationInView:self].y < (self.bounds.size.height - self.bounds.origin.y) / 2) {
            newPage = MAX(1, self.currentPage - 1);
        } else {
            newPage = MIN(self.currentPage + 1, numberOfPages);
        }

        [self setCurrentPage:newPage animated:YES];
    }
}


- (void) panned:(UIPanGestureRecognizer *) recognizer {
    if (animating) {
        return;
    }

    static BOOL hasFailed;
    static BOOL initialized;

    static NSInteger oldPage;



    float translation = [recognizer translationInView:self].y;


    float progress = translation / self.bounds.size.height;


    if (flipDirection == AFKPageFlipperDirectionTop) {
        progress = MIN(progress, 0);
    } else {
        progress = MAX(progress, 0);
    }

    switch (recognizer.state) {
        case UIGestureRecognizerStateBegan:
            hasFailed = FALSE;
            initialized = FALSE;
            animating = NO;
            setNextViewOnCompletion = NO;
            break;


        case UIGestureRecognizerStateChanged:

            if (hasFailed) {
                return;
            }

            if (!initialized) {
                oldPage = self.currentPage;

                if (translation > 0) {
                    if (self.currentPage > 1) {
                        [self doSetCurrentPage:self.currentPage - 1];
                    } else {
                        hasFailed = TRUE;
                        return;
                    }
                } else {
                    if (self.currentPage < numberOfPages) {
                        [self doSetCurrentPage:self.currentPage + 1];
                    } else {
                        hasFailed = TRUE;
                        return;
                    }
                }

                hasFailed = NO;
                initialized = TRUE;
                setNextViewOnCompletion = NO;

                [self initFlip];
            }

            [self setFlipProgress:fabs(progress) setDelegate:NO animate:NO];

            break;


        case UIGestureRecognizerStateFailed:
            [self setFlipProgress:0.0 setDelegate:YES animate:YES];
            currentPage = oldPage;
            break;

        case UIGestureRecognizerStateRecognized:
            if (hasFailed) {
                [self setFlipProgress:0.0 setDelegate:YES animate:YES];
                currentPage = oldPage;

                return;
            }

            if (fabs((translation + [recognizer velocityInView:self].y / 4) / self.bounds.size.height) > 0.5) {
                setNextViewOnCompletion = YES;
                [self setFlipProgress:1.0 setDelegate:YES animate:YES];
            } else {
                [self setFlipProgress:0.0 setDelegate:YES animate:YES];
                currentPage = oldPage;
            }

            break;
        default:
            break;
    }
}


#pragma mark -
#pragma mark Frame management


- (void) setFrame:(CGRect) value {
    super.frame = value;

    numberOfPages = [dataSource numberOfPagesForPageFlipper:self];

    if (self.currentPage > numberOfPages) {
        self.currentPage = numberOfPages;
    }

}


#pragma mark -
#pragma mark Initialization and memory management


+ (Class) layerClass {
    return [CATransformLayer class];
}

- (id)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        _tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
        _panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panned:)];

        [_tapRecognizer requireGestureRecognizerToFail:_panRecognizer];

        [self addGestureRecognizer:_tapRecognizer];
        [self addGestureRecognizer:_panRecognizer];
    }
    return self;
}


- (void)dealloc {
    self.dataSource = Nil;
    self.currentView = Nil;
    self.nextView = Nil;
    self.tapRecognizer = Nil;
    self.panRecognizer = Nil;
    [super dealloc];
}


@end

AFKPageFlipper的becoz,我能够做上面的代码....功劳归于mtabini先生(AFKPageFlipper作者)

答案 1 :(得分:1)

您可以设置负标度,例如:

[theView setTransform:CGAffineTransformMakeScale(1, -1)];

答案 2 :(得分:0)

与Pradeep相同,但有阴影,ARC也适应:

<强> AFKPageFlipper.h

//
//  AFKPageFlipper.h
//  AFKPageFlipper
//
//  Created by Marco Tabini on 10-10-11.
//  Copyright 2010 AFK Studio Partnership. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>


@class AFKPageFlipper;


@protocol AFKPageFlipperDataSource

- (NSInteger) numberOfPagesForPageFlipper:(AFKPageFlipper *) pageFlipper;
- (UIView *) viewForPage:(NSInteger) page inFlipper:(AFKPageFlipper *) pageFlipper;

@end


typedef enum {
    AFKPageFlipperDirectionTop,
    AFKPageFlipperDirectionBottom,
} AFKPageFlipperDirection;



@interface AFKPageFlipper : UIView {
    NSObject <AFKPageFlipperDataSource> *dataSource;
    NSInteger currentPage;
    NSInteger numberOfPages;

    // shadows
    CALayer *frontLayerShadow;
    CALayer *backLayerShadow;
    CALayer *leftLayerShadow;
    CALayer *rightLayerShadow;
    // shadows

    CALayer *backgroundAnimationLayer;
    CALayer *flipAnimationLayer;



    AFKPageFlipperDirection flipDirection;
    float startFlipAngle;
    float endFlipAngle;
    float currentAngle;

    BOOL setNextViewOnCompletion;
    BOOL animating;

    BOOL disabled;
}

@property (nonatomic,retain) NSObject <AFKPageFlipperDataSource> *dataSource;
@property (nonatomic,assign) NSInteger currentPage;

@property (nonatomic, retain) UITapGestureRecognizer *tapRecognizer;
@property (nonatomic, retain) UIPanGestureRecognizer *panRecognizer;

@property (nonatomic,assign) BOOL disabled;

- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated;

@end

<强> AFKPageFlipper.m

//
//  AFKPageFlipper.m
//  AFKPageFlipper
//
//  Created by Marco Tabini on 10-10-12.
//  Copyright 2010 AFK Studio Partnership. All rights reserved.
//

#import "AFKPageFlipper.h"



#pragma mark -
#pragma mark UIView helpers


@interface UIView(Extended)

- (UIImage *) imageByRenderingView;

@end


@implementation UIView(Extended)


- (UIImage *) imageByRenderingView {
    CGFloat oldAlpha = self.alpha;
    self.alpha = 1;
    UIGraphicsBeginImageContext(self.bounds.size);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    self.alpha = oldAlpha;
    return resultingImage;
}

@end


#pragma mark -
#pragma mark Private interface


@interface AFKPageFlipper()

@property (nonatomic,retain) UIView *currentView;
@property (nonatomic,retain) UIView *nextView;

@end


@implementation AFKPageFlipper

@synthesize tapRecognizer = _tapRecognizer;
@synthesize panRecognizer = _panRecognizer;


#pragma mark -
#pragma mark Flip functionality


- (void) initFlip {

    // Create screenshots of view

    UIImage *currentImage = [self.currentView imageByRenderingView];
    UIImage *newImage = [self.nextView imageByRenderingView];

    // Hide existing views

    self.currentView.alpha = 0;
    self.nextView.alpha = 0;

    // Create representational layers

    CGRect rect = self.bounds;
    rect.size.height /= 2;

    backgroundAnimationLayer = [CALayer layer];
    backgroundAnimationLayer.frame = self.bounds;
    backgroundAnimationLayer.zPosition = -300000;

    CALayer *topLayer = [CALayer layer];
    topLayer.frame = rect;
    topLayer.masksToBounds = YES;
    topLayer.contentsGravity = kCAGravityBottom;

    [backgroundAnimationLayer addSublayer:topLayer];

    rect.origin.y = rect.size.height;

    CALayer *bottomLayer = [CALayer layer];
    bottomLayer.frame = rect;
    bottomLayer.masksToBounds = YES;
    bottomLayer.contentsGravity = kCAGravityTop;

    [backgroundAnimationLayer addSublayer:bottomLayer];

    if (flipDirection == AFKPageFlipperDirectionBottom) {
        topLayer.contents = (id) [newImage CGImage];
        bottomLayer.contents = (id) [currentImage CGImage];
    } else {
        topLayer.contents = (id) [currentImage CGImage];
        bottomLayer.contents = (id) [newImage CGImage];
    }

    [self.layer addSublayer:backgroundAnimationLayer];

    rect.origin.y = 0;

    flipAnimationLayer = [CATransformLayer layer];
    flipAnimationLayer.anchorPoint = CGPointMake(0.5, 1);
    flipAnimationLayer.frame = rect;

    [self.layer addSublayer:flipAnimationLayer];

    CALayer *backLayer = [CALayer layer];
    backLayer.frame = flipAnimationLayer.bounds;
    backLayer.doubleSided = NO;
    backLayer.masksToBounds = YES;

    [flipAnimationLayer addSublayer:backLayer];

    CALayer *frontLayer = [CALayer layer];
    frontLayer.frame = flipAnimationLayer.bounds;
    frontLayer.doubleSided = NO;
    frontLayer.masksToBounds = YES;

    frontLayer.transform = CATransform3DMakeRotation(M_PI, 1.0, 0, 0);

    [flipAnimationLayer addSublayer:frontLayer];

    // shadows
    frontLayerShadow = [CALayer layer];
    frontLayerShadow.frame = frontLayer.bounds;
    frontLayerShadow.doubleSided = NO;
    frontLayerShadow.masksToBounds = YES;
    frontLayerShadow.opacity = 0;
    frontLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
    [frontLayer addSublayer:frontLayerShadow];

    backLayerShadow = [CALayer layer];
    backLayerShadow.frame = backLayer.bounds;
    backLayerShadow.doubleSided = NO;
    backLayerShadow.masksToBounds = YES;
    backLayerShadow.opacity = 0;
    backLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
    [backLayer addSublayer:backLayerShadow];


    leftLayerShadow = [CALayer layer];
    leftLayerShadow.frame = topLayer.bounds;
    leftLayerShadow.doubleSided = NO;
    leftLayerShadow.masksToBounds = YES;
    leftLayerShadow.opacity = 0.0;
    leftLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
    [topLayer addSublayer:leftLayerShadow];

    rightLayerShadow = [CALayer layer];
    rightLayerShadow.frame = bottomLayer.bounds;
    rightLayerShadow.doubleSided = NO;
    rightLayerShadow.masksToBounds = YES;
    rightLayerShadow.opacity = 0.0;
    rightLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
    [bottomLayer addSublayer:rightLayerShadow];
    // shadows


    if (flipDirection == AFKPageFlipperDirectionBottom) {
        backLayer.contents = (id) [currentImage CGImage];
        backLayer.contentsGravity = kCAGravityBottom;

        frontLayer.contents = (id) [newImage CGImage];
        frontLayer.contentsGravity = kCAGravityTop;

        CATransform3D transform = CATransform3DMakeRotation(1.1/M_PI, 1.0, 0.0, 0.0);
        transform.m34 = 1.0f / 2500.0f;

        flipAnimationLayer.transform = transform;

        currentAngle = startFlipAngle = 0;
        endFlipAngle = M_PI;
    } else {
        //down
        backLayer.contents = (id) [newImage CGImage];
        backLayer.contentsGravity = kCAGravityBottom;

        frontLayer.contents = (id) [currentImage CGImage];
        frontLayer.contentsGravity = kCAGravityTop;

        CATransform3D transform = CATransform3DMakeRotation(M_PI/1.1, 1.0, 0.0, 0.0);
        transform.m34 = 1.0f / 2500.0f;

        flipAnimationLayer.transform = transform;

        currentAngle = startFlipAngle = M_PI;
        endFlipAngle = 0;
    }
}


- (void) cleanupFlip {
    [backgroundAnimationLayer removeFromSuperlayer];
    [flipAnimationLayer removeFromSuperlayer];

    backgroundAnimationLayer = Nil;
    flipAnimationLayer = Nil;

    animating = NO;

    if (setNextViewOnCompletion) {
        [self.currentView removeFromSuperview];
        self.currentView = self.nextView;
        self.nextView = Nil;
    } else {
        [self.nextView removeFromSuperview];
        self.nextView = Nil;
    }

    self.currentView.alpha = 1;
}


- (void) setFlipProgress:(float) progress setDelegate:(BOOL) setDelegate animate:(BOOL) animate {
    if (animate) {
        animating = YES;
    }

    float newAngle = startFlipAngle + progress * (endFlipAngle - startFlipAngle);

    float duration = animate ? 0.5 * fabs((newAngle - currentAngle) / (endFlipAngle - startFlipAngle)) : 0;

    currentAngle = newAngle;

    CATransform3D endTransform = CATransform3DIdentity;
    endTransform.m34 = 1.0f / 2500.0f;
    endTransform = CATransform3DRotate(endTransform, newAngle, 1.0, 0.0, 0.0);

    [flipAnimationLayer removeAllAnimations];

    // shadows
    //NSLog(@"End flip angle: %.0f, \tstartflip: %.0f, \tprogress: %.2f\tduration: %.2f", endFlipAngle, startFlipAngle,progress, duration);
    CGFloat newShadowOpacity = (0.5 - progress);

    if(newShadowOpacity < 0) {
        newShadowOpacity *= -1;
    }
    if (newShadowOpacity < 0.05) {
        newShadowOpacity = 0;
    }

    // shadows

    if (duration < 0.15) {
        duration = 0.15;
    }

    [UIView animateWithDuration: duration delay: 0 options: UIViewAnimationOptionCurveLinear animations: ^(void) {
        flipAnimationLayer.transform = endTransform;


        if (endFlipAngle < startFlipAngle) {

            if(progress < 0.5) {
                rightLayerShadow.opacity = newShadowOpacity;
                frontLayerShadow.opacity = (0.5 - newShadowOpacity)/2;

            } else {
                backLayerShadow.opacity = (0.5 - newShadowOpacity)/2;
                leftLayerShadow.opacity = newShadowOpacity;
            }

        } else {
            if(progress < 0.5) {
                leftLayerShadow.opacity = newShadowOpacity;
                backLayerShadow.opacity = (0.5 - newShadowOpacity)/2;


            } else {
                frontLayerShadow.opacity = (0.5 - newShadowOpacity)/2;
                rightLayerShadow.opacity = newShadowOpacity;
            }
        }

        // shadows
    } completion: ^(BOOL completion) {

    }];


    if (setDelegate) {
        [self performSelector:@selector(cleanupFlip) withObject:Nil afterDelay:duration];
    }
}


- (void) flipPage {
    [self setFlipProgress:1.0 setDelegate:YES animate:YES];
}


#pragma mark -
#pragma mark Animation management


- (void)animationDidStop:(NSString *) animationID finished:(NSNumber *) finished context:(void *) context {
    [self cleanupFlip];
}


#pragma mark -
#pragma mark Properties


@synthesize currentPage;


- (BOOL) doSetCurrentPage:(NSInteger) value {
    if (value == currentPage) {
        return FALSE;
    }

    flipDirection = value < currentPage ? AFKPageFlipperDirectionBottom : AFKPageFlipperDirectionTop;

    currentPage = value;

    self.nextView = [self.dataSource viewForPage:value inFlipper:self];
    [self addSubview:self.nextView];

    return TRUE;
}

- (void) setCurrentPage:(NSInteger) value {
    if (![self doSetCurrentPage:value]) {
        return;
    }

    setNextViewOnCompletion = YES;
    animating = YES;

    self.nextView.alpha = 0;

    [UIView beginAnimations:@"" context:Nil];
    [UIView setAnimationDuration:0.5];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    self.nextView.alpha = 1;

    [UIView commitAnimations];
}


- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated {
    if (![self doSetCurrentPage:value]) {
        return;
    }

    setNextViewOnCompletion = YES;
    animating = YES;

    if (animated) {
        [self initFlip];
        [self performSelector:@selector(flipPage) withObject:Nil afterDelay:0.091];
    } else {
        [self animationDidStop:Nil finished:[NSNumber numberWithBool:NO] context:Nil];
    }

}


@synthesize dataSource;


- (void) setDataSource:(NSObject <AFKPageFlipperDataSource>*) value {
    if (dataSource) {
        dataSource = nil;
    }

    dataSource = value;
    numberOfPages = [dataSource numberOfPagesForPageFlipper:self];
    currentPage = 0;
    self.currentPage = 1;
}


@synthesize disabled;


- (void) setDisabled:(BOOL) value {
    disabled = value;

    self.userInteractionEnabled = !value;

    for (UIGestureRecognizer *recognizer in self.gestureRecognizers) {
        recognizer.enabled = !value;
    }
}


#pragma mark -
#pragma mark Touch management


- (void) tapped:(UITapGestureRecognizer *) recognizer {
    if (animating || self.disabled) {
        return;
    }

    if (recognizer.state == UIGestureRecognizerStateRecognized) {
        NSInteger newPage;

        if ([recognizer locationInView:self].y < (self.bounds.size.height - self.bounds.origin.y) / 2) {
            newPage = MAX(1, self.currentPage - 1);
        } else {
            newPage = MIN(self.currentPage + 1, numberOfPages);
        }

        [self setCurrentPage:newPage animated:YES];
    }
}


- (void) panned:(UIPanGestureRecognizer *) recognizer {
    if (animating) {
        return;
    }

    static BOOL hasFailed;
    static BOOL initialized;

    static NSInteger oldPage;



    float translation = [recognizer translationInView:self].y;


    float progress = translation / self.bounds.size.height;


    if (flipDirection == AFKPageFlipperDirectionTop) {
        progress = MIN(progress, 0);
    } else {
        progress = MAX(progress, 0);
    }

    switch (recognizer.state) {
        case UIGestureRecognizerStateBegan:
        hasFailed = FALSE;
        initialized = FALSE;
        animating = NO;
        setNextViewOnCompletion = NO;
        break;


        case UIGestureRecognizerStateChanged:

        if (hasFailed) {
            return;
        }

        if (!initialized) {
            oldPage = self.currentPage;

            if (translation > 0) {
                if (self.currentPage > 1) {
                    [self doSetCurrentPage:self.currentPage - 1];
                } else {
                    hasFailed = TRUE;
                    return;
                }
            } else {
                if (self.currentPage < numberOfPages) {
                    [self doSetCurrentPage:self.currentPage + 1];
                } else {
                    hasFailed = TRUE;
                    return;
                }
            }

            hasFailed = NO;
            initialized = TRUE;
            setNextViewOnCompletion = NO;

            [self initFlip];
        }

        [self setFlipProgress:fabs(progress) setDelegate:NO animate:NO];

        break;


        case UIGestureRecognizerStateFailed:
        [self setFlipProgress:0.0 setDelegate:YES animate:YES];
        currentPage = oldPage;
        break;

        case UIGestureRecognizerStateRecognized:
        if (hasFailed) {
            [self setFlipProgress:0.0 setDelegate:YES animate:YES];
            currentPage = oldPage;

            return;
        }

        if (fabs((translation + [recognizer velocityInView:self].y / 4) / self.bounds.size.height) > 0.5) {
            setNextViewOnCompletion = YES;
            [self setFlipProgress:1.0 setDelegate:YES animate:YES];
        } else {
            [self setFlipProgress:0.0 setDelegate:YES animate:YES];
            currentPage = oldPage;
        }

        break;
        default:
        break;
    }
}


#pragma mark -
#pragma mark Frame management

/*
- (void) setFrame:(CGRect) value {
    super.frame = value;

    numberOfPages = [dataSource numberOfPagesForPageFlipper:self];

    if (self.currentPage > numberOfPages) {
        self.currentPage = numberOfPages;
    }

}*/


#pragma mark -
#pragma mark Initialization and memory management


+ (Class) layerClass {
    return [CATransformLayer class];
}

- (id)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        [self initRecognizers];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    if ((self = [super initWithCoder: aDecoder])) {
        [self initRecognizers];
    }
    return self;
}

- (void) initRecognizers {
    _tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
    _panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panned:)];

    [_tapRecognizer requireGestureRecognizerToFail:_panRecognizer];

    [self addGestureRecognizer:_tapRecognizer];
    [self addGestureRecognizer:_panRecognizer];
}


@end