I've been trying to develop a CALayer
based "CardView" object that has two layers that face away from each other to present the two sides of a card. I've been working with things like the doublesided
property of the CALayer
and find the results to be confusing. My base class is CALayer
and I'm adding two sublayers
to it, one with a M_PI/2 transform and both with doublesided = NO
but as it stands, I get the front showing no matter how I rotate the card and the back doesn't show at all. If I don't create the front, the back shows through both sides and the text is in one corner, not 48 point and blurry.
Here's a link to a screenshot showing the card just finishing a full revolution in a UIViewController's
view. You're seeing the back of the front. This should be invisible, and the back should be showing... I think...
https://files.me.com/gazelips/lj2aqo
// CardView.h
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
#define kCardWidth 100.0f
#define kCardHeight 150.0f
@interface CardView : CALayer {
CALayer *cardFront, *cardBack;
}
@property (nonatomic, retain) CALayer *cardFront, *cardBack;
- (id)initWithPosition:(CGPoint)point;
- (void)addPerspective;
- (CALayer *)createFront;
- (CALayer *)createBack;
@end
// CardView.m
#import "CardView.h"
@implementation CardView
@synthesize cardFront, cardBack;
static CATransform3D kPerspectiveTransform;
- (id)initWithPosition:(CGPoint)point {
NSLog(@"--initWithPosition:CardView");
self = [super init];
if (self != nil) {
[self addPerspective];
self.bounds = CGRectMake(0, 0, kCardWidth, kCardHeight);
self.position = point;
self.edgeAntialiasingMask = 0;
self.backgroundColor = [[UIColor clearColor] CGColor];
self.borderColor = [[UIColor blackColor] CGColor];
self.borderWidth = 0.5;
self.doubleSided = YES;
cardBack = [self createBack];
[self addSublayer: cardBack];
cardFront = [self createFront];
[self addSublayer: cardFront];
}
return self;
}
- (void)addPerspective {
NSLog(@"--prepare:CardView");
kPerspectiveTransform = CATransform3DIdentity;
kPerspectiveTransform.m34 = -1.0/800.0;
self.transform = kPerspectiveTransform;
}
- (CALayer *)createFront {
NSLog(@"--createFront:CardView");
CALayer *front = [[CALayer alloc] init];
front.bounds = CGRectMake(0.0f, 0.0f, kCardWidth, kCardHeight);
front.position = CGPointMake(kCardWidth / 2, kCardHeight / 2);
front.edgeAntialiasingMask = 0;
front.backgroundColor = [[UIColor whiteColor] CGColor];
front.cornerRadius = 8 * (kCardHeight/150);
front.borderWidth = 1;
front.borderColor = [[UIColor grayColor] CGColor];
front.doubleSided = NO;
return [front autorelease];
}
- (CALayer *)createBack {
NSLog(@"--createBack:CardView");
CALayer *back = [[CALayer alloc] init];
back.bounds = CGRectMake(0.0f, 0.0f, kCardWidth, kCardHeight);
back.position = CGPointMake(kCardWidth / 2, kCardHeight / 2);
back.backgroundColor = [[UIColor blueColor] CGColor];
back.contentsGravity = kCAGravityResize;
back.masksToBounds = YES;
back.borderWidth = 4 * (kCardHeight/150);
back.borderColor = [[UIColor grayColor] CGColor];;
back.cornerRadius = 8 * (kCardHeight/150);
back.edgeAntialiasingMask = 0;
back.doubleSided = NO;
CATextLayer *textLayer = [CATextLayer layer];
textLayer.font = [UIFont boldSystemFontOfSize:48];
textLayer.bounds = CGRectMake(0.0f, 0.0f, kCardWidth, kCardHeight);
textLayer.position = CGPointMake(50.0f, 75.0f);
textLayer.string = @"Steve";
[back addSublayer:textLayer];
back.transform = CATransform3DMakeRotation(M_PI, 0.0f, 1.0f, 0.0f);
return [back autorelease];
}
@end
The trick ended up being the use of CATransformLayer.
This a a layer that's just for containing other layers (it can't have things like backgroundColor
or borderWidth
). It does however maintain the true 3D relationship of its child layers (i.e., it doesn't flatten them like a regular CALayer
). So you can make two layers, flip one and offset its zPosition
just a hair and put them both in a CATransformLayer
and now you can flip this parent layer six ways from Sunday and the child layers stay locked together and always render properly. Pretty sweet and low overhead!
There is a much easier way to do this, as long as you're only trying to do a normal 'flip'.
Look into the UIView animation system, and there is a 'transitionWithView'. You can apply a flip animation there, and it will provide the same effect, with near no work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With